{
  "schema_version": 1,
  "product": {
    "name": "SocialTap",
    "tagline": "Public social media data through one API key and one JSON schema.",
    "description": "SocialTap returns public social media data (transcripts today; profiles, posts and comments next) from every major platform through one API key, one credit balance and one response schema per resource.",
    "url": "https://staging.socialtap.dev",
    "api_url": "https://staging.socialtap.dev",
    "support_email": "",
    "signup_url": "https://staging.socialtap.dev/signup/",
    "featured_endpoint": "youtube.transcript",
    "test_key_prefix": "st_test_",
    "live_key_prefix": "st_live_",
    "free_credits": 100,
    "test_balance": 10000,
    "turnstile_site_key": "",
    "packs": [
      {
        "id": "25k",
        "credits": 25000,
        "price_cents": 2000
      },
      {
        "id": "100k",
        "credits": 100000,
        "price_cents": 7000
      },
      {
        "id": "500k",
        "credits": 500000,
        "price_cents": 30000
      },
      {
        "id": "2m",
        "credits": 2000000,
        "price_cents": 100000
      }
    ],
    "plans": [
      {
        "id": "payg",
        "name": "Pay as you go",
        "monthly_credits": 0,
        "price_cents": 0,
        "public": true
      }
    ],
    "billing": {
      "failed_requests_free": true,
      "cache_hits_free": true,
      "credits_expire": false
    },
    "credit_price": {
      "min_usd": 0.0005,
      "min_pack": "2m",
      "max_usd": 0.0008,
      "max_pack": "25k"
    }
  },
  "platforms": [
    {
      "id": "facebook",
      "name": "Facebook",
      "website": "https://www.facebook.com",
      "summary": "Transcripts of public Facebook videos and reels, and keyword search over Meta's public Ad Library.",
      "endpoints": [
        "facebook.search_ads",
        "facebook.transcript"
      ],
      "url_forms": [
        {
          "kind": "video",
          "example": "https://www.facebook.com/reel/1803909017420449"
        },
        {
          "kind": "hashtag",
          "example": "https://www.facebook.com/hashtag/cooking"
        },
        {
          "kind": "post",
          "example": "https://www.facebook.com/NASA/posts/pfbid02abc"
        },
        {
          "kind": "profile",
          "example": "https://www.facebook.com/NASA"
        }
      ]
    },
    {
      "id": "instagram",
      "name": "Instagram",
      "website": "https://www.instagram.com",
      "summary": "Public Instagram posts, reels and carousels, and transcripts of reels and video posts.",
      "endpoints": [
        "instagram.post",
        "instagram.transcript"
      ],
      "url_forms": [
        {
          "kind": "video",
          "example": "https://www.instagram.com/reel/De1S3KpCXf0/"
        },
        {
          "kind": "post",
          "example": "https://www.instagram.com/p/C0AbCdEfGhI/"
        },
        {
          "kind": "hashtag",
          "example": "https://www.instagram.com/explore/tags/cooking/"
        },
        {
          "kind": "profile",
          "example": "https://www.instagram.com/natgeo/"
        }
      ]
    },
    {
      "id": "tiktok",
      "name": "TikTok",
      "website": "https://www.tiktok.com",
      "summary": "Public TikTok profiles with exact follower counts, and transcripts of public videos from creator captions, auto-captions or the audio.",
      "endpoints": [
        "tiktok.profile",
        "tiktok.transcript"
      ],
      "url_forms": [
        {
          "kind": "video",
          "example": "https://www.tiktok.com/@scout2015/video/6718335390845095173"
        },
        {
          "kind": "post",
          "example": "https://www.tiktok.com/@nasa/photo/7310000000000000000"
        },
        {
          "kind": "hashtag",
          "example": "https://www.tiktok.com/tag/cooking"
        },
        {
          "kind": "profile",
          "example": "https://www.tiktok.com/@scout2015"
        }
      ]
    },
    {
      "id": "youtube",
      "name": "YouTube",
      "website": "https://www.youtube.com",
      "summary": "Transcripts and comments of public YouTube videos, shorts and live replays.",
      "endpoints": [
        "youtube.comments",
        "youtube.transcript"
      ],
      "url_forms": [
        {
          "kind": "video",
          "example": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
        },
        {
          "kind": "video",
          "example": "https://www.youtube.com/shorts/dQw4w9WgXcQ"
        },
        {
          "kind": "video",
          "example": "https://youtu.be/dQw4w9WgXcQ"
        },
        {
          "kind": "playlist",
          "example": "https://www.youtube.com/playlist?list=PLFgquLnL59alCl_2TQvOiD5Vgm1hCaGSI"
        },
        {
          "kind": "hashtag",
          "example": "https://www.youtube.com/hashtag/cooking"
        },
        {
          "kind": "profile",
          "example": "https://www.youtube.com/@RickAstleyYT"
        }
      ]
    }
  ],
  "resources": [
    {
      "id": "ad",
      "name": "Ad",
      "plural": "Ads",
      "summary": "A public ad library entry: advertiser, creatives, run dates, placements and, where disclosed, reach and spend ranges.",
      "status": "live",
      "errors": [
        "not_found"
      ],
      "fields": [
        {
          "path": "platform",
          "type": "string",
          "nullable": false,
          "description": "Library owner's platform ID (facebook for Meta's library, tiktok, google, linkedin)."
        },
        {
          "path": "id",
          "type": "string",
          "nullable": false,
          "description": "Library ad ID (string)."
        },
        {
          "path": "url",
          "type": "string",
          "format": "uri",
          "nullable": false,
          "description": "Public library page of the ad."
        },
        {
          "path": "advertiser",
          "type": "object",
          "nullable": false,
          "description": "The advertising page, business or account."
        },
        {
          "path": "advertiser.id",
          "type": "string",
          "nullable": true,
          "description": "Platform-native account ID; null when only the handle is known."
        },
        {
          "path": "advertiser.username",
          "type": "string",
          "nullable": true,
          "description": "Handle without the @; null when the account has none."
        },
        {
          "path": "advertiser.display_name",
          "type": "string",
          "nullable": true,
          "description": "Display name shown on the profile."
        },
        {
          "path": "advertiser.url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Canonical profile URL."
        },
        {
          "path": "advertiser.avatar_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Profile picture URL. Platform CDN URLs can expire within hours."
        },
        {
          "path": "advertiser.is_verified",
          "type": "boolean",
          "nullable": true,
          "description": "Platform verification badge; null where the platform has no badge."
        },
        {
          "path": "paid_by",
          "type": "string",
          "nullable": true,
          "description": "Disclosed payer (\"Paid for by\")."
        },
        {
          "path": "is_active",
          "type": "boolean",
          "nullable": true,
          "description": "True while the ad is running."
        },
        {
          "path": "first_shown_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "First delivery, RFC 3339 UTC (often date-only: see approximate_fields)."
        },
        {
          "path": "last_shown_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "Last delivery, RFC 3339 UTC; null while active."
        },
        {
          "path": "type",
          "type": "string",
          "nullable": false,
          "description": "Creative shape. New values may be added; treat unknown values as other.",
          "enum": [
            "image",
            "video",
            "carousel",
            "text",
            "dynamic",
            "other"
          ]
        },
        {
          "path": "publisher_platforms",
          "type": "string[]",
          "nullable": false,
          "description": "Where it ran, lowercase IDs (facebook, instagram, messenger, audience_network, youtube, …); [] if not disclosed."
        },
        {
          "path": "creatives",
          "type": "object[]",
          "nullable": false,
          "description": "The ad's creatives: one per card of a carousel and per variation of a dynamic or catalog ad, else one for the ad itself; [] if none are shown."
        },
        {
          "path": "creatives[].title",
          "type": "string",
          "nullable": true,
          "description": "Headline."
        },
        {
          "path": "creatives[].body",
          "type": "string",
          "nullable": true,
          "description": "Primary text."
        },
        {
          "path": "creatives[].link_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Destination URL."
        },
        {
          "path": "creatives[].link_description",
          "type": "string",
          "nullable": true,
          "description": "Link description text."
        },
        {
          "path": "creatives[].cta_text",
          "type": "string",
          "nullable": true,
          "description": "Call-to-action label (e.g. Shop now)."
        },
        {
          "path": "creatives[].media",
          "type": "object[]",
          "nullable": false,
          "description": "Images and videos of this creative; [] if none."
        },
        {
          "path": "creatives[].media[].type",
          "type": "string",
          "nullable": false,
          "description": "Kind of file. New values may be added; treat unknown values as other.",
          "enum": [
            "image",
            "video",
            "audio",
            "gif"
          ]
        },
        {
          "path": "creatives[].media[].url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Direct file URL; null when the platform withholds it, or when the link would only work from SocialTap's network or with a login. Often signed and expiring: see expires_at."
        },
        {
          "path": "creatives[].media[].expires_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "When url stops working, RFC 3339 UTC, read from the platform's signed URL; null when unknown or when it does not expire."
        },
        {
          "path": "creatives[].media[].thumbnail_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Poster or preview image URL."
        },
        {
          "path": "creatives[].media[].width",
          "type": "integer",
          "nullable": true,
          "description": "Width in pixels."
        },
        {
          "path": "creatives[].media[].height",
          "type": "integer",
          "nullable": true,
          "description": "Height in pixels."
        },
        {
          "path": "creatives[].media[].duration_ms",
          "type": "integer",
          "nullable": true,
          "description": "Duration in milliseconds (video and audio only)."
        },
        {
          "path": "creatives[].media[].alt_text",
          "type": "string",
          "nullable": true,
          "description": "Accessibility text provided by the author or platform."
        },
        {
          "path": "languages",
          "type": "string[]",
          "nullable": false,
          "description": "BCP-47 languages of the ad; [] if not disclosed."
        },
        {
          "path": "country_codes",
          "type": "string[]",
          "nullable": false,
          "description": "ISO 3166-1 alpha-2 countries where it was shown; [] if not disclosed."
        },
        {
          "path": "is_political",
          "type": "boolean",
          "nullable": true,
          "description": "Categorized as a political or social-issue ad; false when the library files it under another category, null when it shows none."
        },
        {
          "path": "impressions",
          "type": "object",
          "nullable": true,
          "description": "Impressions or times shown range; null when not disclosed."
        },
        {
          "path": "impressions.lower_bound",
          "type": "integer",
          "nullable": false,
          "description": "Inclusive lower bound."
        },
        {
          "path": "impressions.upper_bound",
          "type": "integer",
          "nullable": true,
          "description": "Inclusive upper bound; null when open-ended (e.g. \">1M\"). Equal to lower_bound when exact."
        },
        {
          "path": "reach",
          "type": "object",
          "nullable": true,
          "description": "Unique users reached range; null when not disclosed."
        },
        {
          "path": "reach.lower_bound",
          "type": "integer",
          "nullable": false,
          "description": "Inclusive lower bound."
        },
        {
          "path": "reach.upper_bound",
          "type": "integer",
          "nullable": true,
          "description": "Inclusive upper bound; null when open-ended (e.g. \">1M\"). Equal to lower_bound when exact."
        },
        {
          "path": "audience_size",
          "type": "object",
          "nullable": true,
          "description": "Estimated target audience size range; null when not disclosed."
        },
        {
          "path": "audience_size.lower_bound",
          "type": "integer",
          "nullable": false,
          "description": "Inclusive lower bound."
        },
        {
          "path": "audience_size.upper_bound",
          "type": "integer",
          "nullable": true,
          "description": "Inclusive upper bound; null when open-ended (e.g. \">1M\"). Equal to lower_bound when exact."
        },
        {
          "path": "spend",
          "type": "object",
          "nullable": true,
          "description": "Spend range; null when not disclosed."
        },
        {
          "path": "spend.lower_bound",
          "type": "integer",
          "nullable": false,
          "description": "Inclusive lower bound, in whole units of currency."
        },
        {
          "path": "spend.upper_bound",
          "type": "integer",
          "nullable": true,
          "description": "Inclusive upper bound in whole units of currency; null when open-ended."
        },
        {
          "path": "spend.currency",
          "type": "string",
          "nullable": false,
          "description": "ISO 4217 currency code, uppercase (USD)."
        },
        {
          "path": "approximate_fields",
          "type": "string[]",
          "nullable": false,
          "description": "JSON paths of values that are rounded, date-only or parsed from display text; [] when all exact."
        }
      ],
      "endpoints": [
        "facebook.search_ads"
      ]
    },
    {
      "id": "comment",
      "name": "Comment",
      "plural": "Comments",
      "summary": "A public comment or reply on a post, with its author, thread position and like/reply counts.",
      "status": "live",
      "errors": [
        "not_found",
        "private_or_unavailable"
      ],
      "fields": [
        {
          "path": "platform",
          "type": "string",
          "nullable": false,
          "description": "Platform ID (tiktok, youtube, …)."
        },
        {
          "path": "id",
          "type": "string",
          "nullable": false,
          "description": "Platform-native comment ID (string; Reddit without t1_)."
        },
        {
          "path": "post_id",
          "type": "string",
          "nullable": false,
          "description": "ID of the post commented on (equals post.id)."
        },
        {
          "path": "parent_id",
          "type": "string",
          "nullable": true,
          "description": "ID of the comment this replies to; null for top-level comments."
        },
        {
          "path": "url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Permalink, when the platform has one."
        },
        {
          "path": "text",
          "type": "string",
          "nullable": false,
          "description": "Comment body as plain text."
        },
        {
          "path": "author",
          "type": "object",
          "nullable": false,
          "description": "The commenting account."
        },
        {
          "path": "author.id",
          "type": "string",
          "nullable": true,
          "description": "Platform-native account ID; null when only the handle is known."
        },
        {
          "path": "author.username",
          "type": "string",
          "nullable": true,
          "description": "Handle without the @; null when the account has none."
        },
        {
          "path": "author.display_name",
          "type": "string",
          "nullable": true,
          "description": "Display name shown on the profile."
        },
        {
          "path": "author.url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Canonical profile URL."
        },
        {
          "path": "author.avatar_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Profile picture URL. Platform CDN URLs can expire within hours."
        },
        {
          "path": "author.is_verified",
          "type": "boolean",
          "nullable": true,
          "description": "Platform verification badge; null where the platform has no badge."
        },
        {
          "path": "published_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "When the comment was posted, RFC 3339 UTC. Where the platform shows only a relative time (\"3 weeks ago\"), the latest time that label allows (the fetch time minus the stated amount), listed in approximate_fields."
        },
        {
          "path": "edited_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "When the comment was last edited, RFC 3339 UTC; null when never edited or when the platform marks edits without a time (see is_edited)."
        },
        {
          "path": "is_edited",
          "type": "boolean",
          "nullable": true,
          "description": "The comment was edited after posting (the platform's edited mark); null when the platform does not say."
        },
        {
          "path": "is_pinned",
          "type": "boolean",
          "nullable": true,
          "description": "Pinned by the post's author."
        },
        {
          "path": "is_liked_by_author",
          "type": "boolean",
          "nullable": true,
          "description": "Liked or hearted by the post's author."
        },
        {
          "path": "is_by_post_author",
          "type": "boolean",
          "nullable": true,
          "description": "Written by the post's author (the creator's own comment, marked as such by the platform)."
        },
        {
          "path": "metrics",
          "type": "object",
          "nullable": false,
          "description": "Counts at fetch time; null = hidden or not exposed, 0 = zero."
        },
        {
          "path": "metrics.like_count",
          "type": "integer",
          "nullable": true,
          "description": "Likes or upvotes; never negative."
        },
        {
          "path": "metrics.reply_count",
          "type": "integer",
          "nullable": true,
          "description": "Direct replies."
        },
        {
          "path": "approximate_fields",
          "type": "string[]",
          "nullable": false,
          "description": "JSON paths of values rounded by the platform or parsed from display text; [] when all exact."
        }
      ],
      "endpoints": [
        "youtube.comments"
      ]
    },
    {
      "id": "post",
      "name": "Post",
      "plural": "Posts",
      "summary": "A public post (video, reel, short, photo, carousel, tweet or text) with its author, media and engagement metrics.",
      "status": "live",
      "errors": [
        "not_found",
        "private_or_unavailable"
      ],
      "fields": [
        {
          "path": "platform",
          "type": "string",
          "nullable": false,
          "description": "Platform ID (tiktok, youtube, …)."
        },
        {
          "path": "id",
          "type": "string",
          "nullable": false,
          "description": "Platform-native post ID (string; Instagram shortcode, Reddit without t3_)."
        },
        {
          "path": "url",
          "type": "string",
          "format": "uri",
          "nullable": false,
          "description": "Canonical post URL."
        },
        {
          "path": "type",
          "type": "string",
          "nullable": false,
          "description": "Normalized content shape. New values may be added; treat unknown values as other.",
          "enum": [
            "video",
            "image",
            "carousel",
            "text",
            "link",
            "audio"
          ]
        },
        {
          "path": "platform_type",
          "type": "string",
          "nullable": true,
          "description": "The platform's own product label, lowercase (reel, short, story, live, tweet, …)."
        },
        {
          "path": "title",
          "type": "string",
          "nullable": true,
          "description": "Headline (YouTube title, Reddit title); null on caption-only platforms."
        },
        {
          "path": "text",
          "type": "string",
          "nullable": true,
          "description": "Caption, description or body as plain text."
        },
        {
          "path": "language",
          "type": "string",
          "nullable": true,
          "description": "BCP-47 language of text, when the platform states it."
        },
        {
          "path": "published_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "When the post went public, RFC 3339 UTC."
        },
        {
          "path": "author",
          "type": "object",
          "nullable": false,
          "description": "The posting account."
        },
        {
          "path": "author.id",
          "type": "string",
          "nullable": true,
          "description": "Platform-native account ID; null when only the handle is known."
        },
        {
          "path": "author.username",
          "type": "string",
          "nullable": true,
          "description": "Handle without the @; null when the account has none."
        },
        {
          "path": "author.display_name",
          "type": "string",
          "nullable": true,
          "description": "Display name shown on the profile."
        },
        {
          "path": "author.url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Canonical profile URL."
        },
        {
          "path": "author.avatar_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Profile picture URL. Platform CDN URLs can expire within hours."
        },
        {
          "path": "author.is_verified",
          "type": "boolean",
          "nullable": true,
          "description": "Platform verification badge; null where the platform has no badge."
        },
        {
          "path": "coauthors",
          "type": "object[]",
          "nullable": false,
          "description": "Other accounts credited as co-authors of the post (Instagram and Facebook collabs, YouTube collaborations), in the platform's order, author excluded; [] if none."
        },
        {
          "path": "coauthors[].id",
          "type": "string",
          "nullable": true,
          "description": "Platform-native account ID; null when only the handle is known."
        },
        {
          "path": "coauthors[].username",
          "type": "string",
          "nullable": true,
          "description": "Handle without the @; null when the account has none."
        },
        {
          "path": "coauthors[].display_name",
          "type": "string",
          "nullable": true,
          "description": "Display name shown on the profile."
        },
        {
          "path": "coauthors[].url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Canonical profile URL."
        },
        {
          "path": "coauthors[].avatar_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Profile picture URL. Platform CDN URLs can expire within hours."
        },
        {
          "path": "coauthors[].is_verified",
          "type": "boolean",
          "nullable": true,
          "description": "Platform verification badge; null where the platform has no badge."
        },
        {
          "path": "duration_ms",
          "type": "integer",
          "nullable": true,
          "description": "Length of the primary video or audio in milliseconds; null for other posts."
        },
        {
          "path": "thumbnail_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Cover or poster image URL."
        },
        {
          "path": "media",
          "type": "object[]",
          "nullable": false,
          "description": "Attachments in display order (carousel items); [] for text posts."
        },
        {
          "path": "media[].type",
          "type": "string",
          "nullable": false,
          "description": "Kind of file. New values may be added; treat unknown values as other.",
          "enum": [
            "image",
            "video",
            "audio",
            "gif"
          ]
        },
        {
          "path": "media[].url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Direct file URL; null when the platform withholds it, or when the link would only work from SocialTap's network or with a login. Often signed and expiring: see expires_at."
        },
        {
          "path": "media[].expires_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "When url stops working, RFC 3339 UTC, read from the platform's signed URL; null when unknown or when it does not expire."
        },
        {
          "path": "media[].thumbnail_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Poster or preview image URL."
        },
        {
          "path": "media[].width",
          "type": "integer",
          "nullable": true,
          "description": "Width in pixels."
        },
        {
          "path": "media[].height",
          "type": "integer",
          "nullable": true,
          "description": "Height in pixels."
        },
        {
          "path": "media[].duration_ms",
          "type": "integer",
          "nullable": true,
          "description": "Duration in milliseconds (video and audio only)."
        },
        {
          "path": "media[].alt_text",
          "type": "string",
          "nullable": true,
          "description": "Accessibility text provided by the author or platform."
        },
        {
          "path": "hashtags",
          "type": "string[]",
          "nullable": false,
          "description": "Hashtags without #, case kept; [] if none."
        },
        {
          "path": "mentions",
          "type": "string[]",
          "nullable": false,
          "description": "Mentioned or tagged usernames without @; [] if none."
        },
        {
          "path": "is_pinned",
          "type": "boolean",
          "nullable": true,
          "description": "Pinned to the author's profile."
        },
        {
          "path": "is_sponsored",
          "type": "boolean",
          "nullable": true,
          "description": "Labeled as a paid partnership or branded content."
        },
        {
          "path": "is_nsfw",
          "type": "boolean",
          "nullable": true,
          "description": "Marked adult or sensitive by the platform."
        },
        {
          "path": "metrics",
          "type": "object",
          "nullable": false,
          "description": "Counts at fetch time; null = hidden or not exposed, 0 = zero."
        },
        {
          "path": "metrics.view_count",
          "type": "integer",
          "nullable": true,
          "description": "Views or plays."
        },
        {
          "path": "metrics.like_count",
          "type": "integer",
          "nullable": true,
          "description": "Likes, hearts, total reactions or upvotes; never negative. Null when hidden by the owner."
        },
        {
          "path": "metrics.comment_count",
          "type": "integer",
          "nullable": true,
          "description": "Comments and replies."
        },
        {
          "path": "metrics.share_count",
          "type": "integer",
          "nullable": true,
          "description": "Shares or sends (not reposts)."
        },
        {
          "path": "metrics.repost_count",
          "type": "integer",
          "nullable": true,
          "description": "Reposts, retweets or reshares."
        },
        {
          "path": "metrics.quote_count",
          "type": "integer",
          "nullable": true,
          "description": "Quote posts."
        },
        {
          "path": "metrics.save_count",
          "type": "integer",
          "nullable": true,
          "description": "Saves, bookmarks or favorites."
        },
        {
          "path": "approximate_fields",
          "type": "string[]",
          "nullable": false,
          "description": "JSON paths of values rounded by the platform or parsed from display text; [] when all exact."
        }
      ],
      "endpoints": [
        "instagram.post"
      ]
    },
    {
      "id": "profile",
      "name": "Profile",
      "plural": "Profiles",
      "summary": "A public account (creator, channel, page, company or community) with its bio, links and follower metrics.",
      "status": "live",
      "errors": [
        "not_found",
        "private_or_unavailable"
      ],
      "fields": [
        {
          "path": "platform",
          "type": "string",
          "nullable": false,
          "description": "Platform ID (tiktok, youtube, …)."
        },
        {
          "path": "id",
          "type": "string",
          "nullable": false,
          "description": "Platform-native account ID (string; YouTube UC…, Reddit without t2_)."
        },
        {
          "path": "username",
          "type": "string",
          "nullable": true,
          "description": "Handle without the @; null when the account has none."
        },
        {
          "path": "display_name",
          "type": "string",
          "nullable": true,
          "description": "Display name, channel title or page name; null when empty or not exposed."
        },
        {
          "path": "url",
          "type": "string",
          "format": "uri",
          "nullable": false,
          "description": "Canonical profile URL."
        },
        {
          "path": "type",
          "type": "string",
          "nullable": false,
          "description": "Coarse account kind, as the platform labels it; unknown when it does not (TikTok labels organizations only). New values may be added; treat unknown values as unknown.",
          "enum": [
            "person",
            "organization",
            "community",
            "unknown"
          ]
        },
        {
          "path": "bio",
          "type": "string",
          "nullable": true,
          "description": "Profile bio, channel description or page about text; null when empty or not exposed."
        },
        {
          "path": "avatar_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Profile picture URL (largest available). Platform CDN URLs can expire within hours."
        },
        {
          "path": "banner_url",
          "type": "string",
          "format": "uri",
          "nullable": true,
          "description": "Header or cover image URL."
        },
        {
          "path": "is_verified",
          "type": "boolean",
          "nullable": true,
          "description": "Platform verification badge; null where the platform has no badge."
        },
        {
          "path": "is_private",
          "type": "boolean",
          "nullable": true,
          "description": "True when the account's posts are not public."
        },
        {
          "path": "category",
          "type": "string",
          "nullable": true,
          "description": "Platform category label (e.g. Musician, Media & Entertainment), as shown; not normalized. Often set only for business or creator accounts."
        },
        {
          "path": "external_links",
          "type": "object[]",
          "nullable": false,
          "description": "Links in the bio or channel links; [] if none or not exposed."
        },
        {
          "path": "external_links[].url",
          "type": "string",
          "format": "uri",
          "nullable": false,
          "description": "Link target, absolute (https:// is added where the platform stores the link without a scheme)."
        },
        {
          "path": "external_links[].title",
          "type": "string",
          "nullable": true,
          "description": "Link label shown by the platform."
        },
        {
          "path": "location",
          "type": "string",
          "nullable": true,
          "description": "Self-declared location text."
        },
        {
          "path": "country_code",
          "type": "string",
          "nullable": true,
          "description": "ISO 3166-1 alpha-2 country the platform states for the account."
        },
        {
          "path": "created_at",
          "type": "string",
          "format": "date-time",
          "nullable": true,
          "description": "Account creation or join date, RFC 3339 UTC (some platforms give only the day: see approximate_fields)."
        },
        {
          "path": "metrics",
          "type": "object",
          "nullable": false,
          "description": "Counts at fetch time; null = hidden or not exposed, 0 = zero."
        },
        {
          "path": "metrics.follower_count",
          "type": "integer",
          "nullable": true,
          "description": "Followers or subscribers."
        },
        {
          "path": "metrics.following_count",
          "type": "integer",
          "nullable": true,
          "description": "Accounts this profile follows."
        },
        {
          "path": "metrics.post_count",
          "type": "integer",
          "nullable": true,
          "description": "Posts or videos as the platform counts them on the profile (for a private account, posts the caller cannot see)."
        },
        {
          "path": "metrics.total_like_count",
          "type": "integer",
          "nullable": true,
          "description": "Likes received across all of the profile's posts (not likes given)."
        },
        {
          "path": "metrics.total_view_count",
          "type": "integer",
          "nullable": true,
          "description": "Lifetime views across all of the profile's posts."
        },
        {
          "path": "approximate_fields",
          "type": "string[]",
          "nullable": false,
          "description": "JSON paths of values rounded by the platform or parsed from display text (e.g. metrics.follower_count); [] when all exact."
        }
      ],
      "endpoints": [
        "tiktok.profile"
      ]
    },
    {
      "id": "transcript",
      "name": "Transcript",
      "plural": "Transcripts",
      "summary": "Timed transcript of a public video: the platform's caption track, or speech-to-text on its audio when there is none.",
      "status": "live",
      "errors": [
        "not_found",
        "private_or_unavailable",
        "no_transcript"
      ],
      "fields": [
        {
          "path": "platform",
          "type": "string",
          "nullable": false,
          "description": "Platform the media is hosted on."
        },
        {
          "path": "id",
          "type": "string",
          "nullable": false,
          "description": "Platform-native ID of the post (equals post.id)."
        },
        {
          "path": "url",
          "type": "string",
          "format": "uri",
          "nullable": false,
          "description": "Canonical URL of the post."
        },
        {
          "path": "language",
          "type": "string",
          "nullable": true,
          "description": "BCP-47 language of the returned transcript; null when unknown."
        },
        {
          "path": "source",
          "type": "string",
          "nullable": false,
          "description": "captions = platform caption track; asr = speech-to-text on the audio.",
          "enum": [
            "captions",
            "asr"
          ]
        },
        {
          "path": "is_auto_generated",
          "type": "boolean",
          "nullable": false,
          "description": "True when the track was machine-generated (platform auto-captions or ASR)."
        },
        {
          "path": "available_languages",
          "type": "string[]",
          "nullable": false,
          "description": "BCP-47 caption languages the platform offers for this post; [] if none or not listed."
        },
        {
          "path": "duration_ms",
          "type": "integer",
          "nullable": true,
          "description": "Media duration in milliseconds; null when unknown."
        },
        {
          "path": "segments",
          "type": "object[]",
          "nullable": false,
          "description": "Timed cues in order; never empty on success."
        },
        {
          "path": "segments[].start_ms",
          "type": "integer",
          "nullable": false,
          "description": "Start time in milliseconds from the beginning of the media."
        },
        {
          "path": "segments[].end_ms",
          "type": "integer",
          "nullable": false,
          "description": "End time in milliseconds from the beginning of the media."
        },
        {
          "path": "segments[].text",
          "type": "string",
          "nullable": false,
          "description": "Cue text, whitespace-normalized."
        },
        {
          "path": "text",
          "type": "string",
          "nullable": false,
          "description": "Full transcript: segment texts joined with single spaces."
        }
      ],
      "endpoints": [
        "facebook.transcript",
        "instagram.transcript",
        "tiktok.transcript",
        "youtube.transcript"
      ]
    }
  ],
  "endpoints": [
    {
      "id": "facebook.search_ads",
      "platform": "facebook",
      "name": "search_ads",
      "resource": "ad",
      "list": {
        "page_max": 10,
        "cursor_ttl_s": 3600,
        "order": "most impressions first"
      },
      "path": "/v1/facebook/search_ads",
      "method": "GET",
      "operation_id": "facebook.search_ads.list",
      "status": "beta",
      "title": "Facebook Ad Library Search API",
      "summary": "Search Meta's Ad Library by keyword",
      "description": "Searches Meta's public Ad Library by keyword and returns the matching ads that ran on Facebook, Instagram, Messenger, Threads and the Audience Network: the advertiser's page, the days the ad first and last ran, whether it is still active, where it ran, and each creative's headline, text, link, call to action, images and videos. Political and social-issue ads also carry the disclosed payer and Meta's impressions, reach and spend ranges. Filter by country, active status and the day an ad first ran; results come in the Ad Library's own order, most impressions first.",
      "use_cases": [
        "Monitor competitors' ad creatives and offers",
        "Research ad copy in a market or niche",
        "Track political and issue ad spending",
        "Find the advertisers running ads on a topic"
      ],
      "notes": [
        "Payer, impressions, reach and spend are disclosed only for political and social-issue ads; they are null for other ads.",
        "Dates are whole days (Meta reports the day an ad first and last ran, US Pacific time), so they are listed in approximate_fields.",
        "since and until filter by the day an ad first ran (first_shown_at); a filtered page can hold fewer ads than the page size, and that is not the end of the list.",
        "Ads that share one creative are grouped by Meta; each ad of a group is returned as its own item.",
        "Languages, targeting and EU reach by country are not part of the search results; those fields are empty or null."
      ],
      "faq": [
        {
          "q": "How do I search for an exact phrase?",
          "a": "Wrap the query in double quotes, for example \"cold brew\"; without quotes every word must appear, in any order."
        },
        {
          "q": "Which ads does it find?",
          "a": "Every ad in Meta's public Ad Library whose text matches the query: active and inactive ads on all of Meta's apps, from any advertiser."
        }
      ],
      "keywords": [
        "meta ad library api",
        "facebook ad library scraper",
        "facebook ads search api",
        "instagram ad library api",
        "competitor ads api"
      ],
      "params": [
        {
          "name": "query",
          "type": "string",
          "required": true,
          "description": "Keywords matched against the ad's text, in any order. Wrap the query in double quotes (\"cold brew\") to match an exact phrase.",
          "example": "coffee",
          "max_length": 200
        },
        {
          "name": "country",
          "type": "string",
          "required": false,
          "description": "Region: an ISO 3166-1 alpha-2 country code such as US or BR (case-insensitive). Only ads delivered in this country. All countries when omitted.",
          "example": "US",
          "format": "iso3166-1-alpha-2",
          "vocabulary": "country"
        },
        {
          "name": "status",
          "type": "string",
          "required": false,
          "description": "Only ads that are still running (active), only ads that stopped (inactive), or both (all, the default).",
          "example": "active",
          "enum": [
            "all",
            "active",
            "inactive"
          ]
        },
        {
          "name": "since",
          "type": "string",
          "required": false,
          "description": "Only items at or after this time: RFC 3339 (2026-10-01T12:00:00Z) or a date (2026-10-01, midnight UTC). On newest-first lists, paging stops when it is reached, so later pages are not fetched or charged. Filters by first_shown_at, the day the ad first ran.",
          "example": "2026-10-01",
          "format": "date-time",
          "vocabulary": "since"
        },
        {
          "name": "until",
          "type": "string",
          "required": false,
          "description": "Only items before this time (exclusive): RFC 3339 or a date (midnight UTC). Filters by first_shown_at, the day the ad first ran.",
          "example": "2026-10-08",
          "format": "date-time",
          "vocabulary": "until"
        }
      ],
      "sources": [
        "api"
      ],
      "pricing": [
        {
          "kind": "api",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        }
      ],
      "min_credits": 1,
      "max_credits": 1,
      "errors": [
        "invalid_input",
        "invalid_cursor",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "country": "US",
          "query": "coffee",
          "status": "all"
        },
        "response": {
          "data": [
            {
              "advertiser": {
                "avatar_url": "https://scontent-atl3-2.xx.fbcdn.net/v/t39.35426-6/739782028_1533198008291919_1079542453788497101_n.jpg?stp=dst-jpg_s60x60_tt6&_nc_cat=104&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-2.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED&…",
                "display_name": "American Health Support Community",
                "id": "966175593248497",
                "is_verified": null,
                "url": "https://www.facebook.com/61588018263996",
                "username": null
              },
              "approximate_fields": [
                "first_shown_at"
              ],
              "audience_size": null,
              "country_codes": [],
              "creatives": [
                {
                  "body": "My aunt called me a creep for putting a camera in Nana’s living room.\n\nNana called me practical.\n\nThen the camera caught something worse than a fall.\n\nI need to be clear about one thing before anyone decides what kind of grandson I am.\n\nNana asked for the camera.\n\nNot me.\n\nNot my…",
                  "cta_text": "Learn more",
                  "link_description": null,
                  "link_url": "https://shop.pipitea.com/hbt/kd/sp-nm",
                  "media": [
                    {
                      "alt_text": null,
                      "duration_ms": null,
                      "expires_at": null,
                      "height": null,
                      "thumbnail_url": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.35426-6/742001725_2100792434176297_9060645049521596104_n.jpg?stp=dst-jpg_s600x600_tt6&_nc_cat=100&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBE…",
                      "type": "image",
                      "url": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.35426-6/742501415_1706621370582971_4085631001896338504_n.jpg?_nc_cat=103&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED&oe=SCRUBBED",
                      "width": null
                    }
                  ],
                  "title": "Natural Relief in Weeks"
                }
              ],
              "first_shown_at": "2026-07-18T07:00:00Z",
              "id": "2619351891814847",
              "impressions": null,
              "is_active": true,
              "is_political": false,
              "languages": [],
              "last_shown_at": null,
              "paid_by": null,
              "platform": "facebook",
              "publisher_platforms": [
                "facebook",
                "instagram",
                "audience_network"
              ],
              "reach": null,
              "spend": null,
              "type": "image",
              "url": "https://www.facebook.com/ads/library/?id=2619351891814847"
            },
            {
              "advertiser": {
                "avatar_url": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.35426-6/712623693_1599994994434095_1320701223312676781_n.jpg?stp=dst-jpg_s60x60_tt6&_nc_cat=110&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED&…",
                "display_name": "American Health Support Community",
                "id": "966175593248497",
                "is_verified": null,
                "url": "https://www.facebook.com/61588018263996",
                "username": null
              },
              "approximate_fields": [
                "first_shown_at"
              ],
              "audience_size": null,
              "country_codes": [],
              "creatives": [
                {
                  "body": "I almost looked past the woman who saved my husband's life. She was seventy-nine, sitting alone at the booth next to ours at the Cracker Barrel off Highway 31. A glass of milk in front of her. She kept stirring it. She wasn't eating.\n\nIf I'd known, the night I leaned over and ask…",
                  "cta_text": "Learn more",
                  "link_description": "\"I've tried everything to support my kidney function naturally. Fish oil, CoQ10, cutting protein, stressing about every meal. Nothing moved the needle enough to satisfy my doctor. Hibiscus tea is the first thing that actually showed up in my numbers. It's been 3 months and my eGF…",
                  "link_url": "https://shop.pipitea.com/hbt/kd/sp",
                  "media": [
                    {
                      "alt_text": null,
                      "duration_ms": null,
                      "expires_at": null,
                      "height": null,
                      "thumbnail_url": "https://scontent-atl3-2.xx.fbcdn.net/v/t39.35426-6/711729685_1717035472767079_186136568392095276_n.jpg?stp=dst-jpg_s600x600_tt6&_nc_cat=104&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-2.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED…",
                      "type": "image",
                      "url": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.35426-6/713729791_868879862958547_7409366389733397444_n.jpg?_nc_cat=107&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED&oe=SCRUBBED",
                      "width": null
                    }
                  ],
                  "title": "Natural Relief in Weeks"
                }
              ],
              "first_shown_at": "2026-06-01T07:00:00Z",
              "id": "1923645134954720",
              "impressions": null,
              "is_active": true,
              "is_political": false,
              "languages": [],
              "last_shown_at": null,
              "paid_by": null,
              "platform": "facebook",
              "publisher_platforms": [
                "facebook",
                "instagram",
                "audience_network"
              ],
              "reach": null,
              "spend": null,
              "type": "image",
              "url": "https://www.facebook.com/ads/library/?id=1923645134954720"
            },
            {
              "advertiser": {
                "avatar_url": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.35426-6/712025330_1303417705276292_736671271727941526_n.jpg?stp=dst-jpg_s60x60_tt6&_nc_cat=103&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED&o…",
                "display_name": "American Health Support Community",
                "id": "966175593248497",
                "is_verified": null,
                "url": "https://www.facebook.com/61588018263996",
                "username": null
              },
              "approximate_fields": [
                "first_shown_at"
              ],
              "audience_size": null,
              "country_codes": [],
              "creatives": [
                {
                  "body": "I almost looked past the woman who saved my husband's life. She was seventy-nine, sitting alone at the booth next to ours at the Cracker Barrel off Highway 31. A glass of milk in front of her. She kept stirring it. She wasn't eating.\n\nIf I'd known, the night I leaned over and ask…",
                  "cta_text": "Learn more",
                  "link_description": "\"I've tried everything to support my kidney function naturally. Fish oil, CoQ10, cutting protein, stressing about every meal. Nothing moved the needle enough to satisfy my doctor. Hibiscus tea is the first thing that actually showed up in my numbers. It's been 3 months and my eGF…",
                  "link_url": "https://shop.pipitea.com/hbt/kd/sp",
                  "media": [
                    {
                      "alt_text": null,
                      "duration_ms": null,
                      "expires_at": null,
                      "height": null,
                      "thumbnail_url": "https://scontent-atl3-1.xx.fbcdn.net/v/t39.35426-6/711737708_4485707625086258_2353150069319301753_n.jpg?stp=dst-jpg_s600x600_tt6&_nc_cat=109&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-1.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBE…",
                      "type": "image",
                      "url": "https://scontent-atl3-2.xx.fbcdn.net/v/t39.35426-6/710755472_911440771966686_1736050619245168045_n.jpg?_nc_cat=111&ccb=SCRUBBED&_nc_sid=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=14&_nc_ht=scontent-atl3-2.xx&_nc_gid=SCRUBBED&_nc_ss=7b289&oh=SCRUBBED&oe=SCRUBBED",
                      "width": null
                    }
                  ],
                  "title": "Natural Relief in Weeks"
                }
              ],
              "first_shown_at": "2026-06-01T07:00:00Z",
              "id": "2140497416496021",
              "impressions": null,
              "is_active": true,
              "is_political": false,
              "languages": [],
              "last_shown_at": null,
              "paid_by": null,
              "platform": "facebook",
              "publisher_platforms": [
                "facebook",
                "instagram",
                "audience_network"
              ],
              "reach": null,
              "spend": null,
              "type": "image",
              "url": "https://www.facebook.com/ads/library/?id=2140497416496021"
            }
          ],
          "page": {
            "has_more": true,
            "next_cursor": "c1.EXAMPLE_CURSOR",
            "next_cursor_expires_at": "2026-10-26T12:00:00Z",
            "end_reason": null
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "facebook",
            "source": "api",
            "is_cached": false,
            "credits_charged": 1,
            "latency_ms": 0,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": null,
            "credits_remaining": 9999,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "coffee ads in the US",
          "params": {
            "country": "US",
            "query": "coffee"
          },
          "tags": [
            "keyword",
            "country"
          ]
        },
        {
          "name": "exact phrase, active ads only",
          "params": {
            "country": "US",
            "query": "\"cold brew\"",
            "status": "active"
          },
          "tags": [
            "exact_phrase",
            "active"
          ]
        },
        {
          "name": "political ads with disclosed spend",
          "params": {
            "country": "US",
            "query": "voting information center"
          },
          "tags": [
            "political"
          ]
        },
        {
          "name": "ads first shown in September 2026",
          "params": {
            "country": "US",
            "query": "coffee",
            "since": "2026-09-01",
            "until": "2026-10-01"
          },
          "tags": [
            "window"
          ]
        },
        {
          "name": "Japanese keywords",
          "params": {
            "country": "JP",
            "query": "コーヒー"
          },
          "tags": [
            "non_english"
          ]
        },
        {
          "name": "no matching ads",
          "params": {
            "country": "US",
            "query": "zzqxjvbnmqwplkk"
          },
          "tags": [
            "no_results"
          ]
        },
        {
          "name": "country the Ad Library does not cover",
          "params": {
            "country": "XX",
            "query": "coffee"
          },
          "tags": [
            "invalid"
          ]
        }
      ],
      "stats": null,
      "lifecycle": null,
      "identity": "query",
      "accepts": [],
      "accepted_params": [
        "query",
        "country",
        "status",
        "since",
        "until",
        "max_age",
        "fields",
        "limit",
        "cursor"
      ]
    },
    {
      "id": "facebook.transcript",
      "platform": "facebook",
      "name": "transcript",
      "resource": "transcript",
      "list": null,
      "path": "/v1/facebook/transcript",
      "method": "GET",
      "operation_id": "facebook.transcript.get",
      "status": "beta",
      "title": "Facebook Video Transcript API",
      "summary": "Facebook video transcript",
      "description": "Returns the timed transcript of a public Facebook video or reel: the uploaded or auto-generated caption track when Facebook has one, otherwise speech-to-text on the video's audio. Accepts reel, watch, page video, video.php, fb.watch and share links, or a numeric video ID.",
      "use_cases": [
        "Analyze public page and creator videos",
        "Make Facebook video content searchable",
        "Repurpose reels and videos into text",
        "Feed video content into LLM pipelines"
      ],
      "notes": [
        "Videos that Facebook only serves to logged-in users return private_or_unavailable."
      ],
      "faq": [],
      "keywords": [
        "facebook video captions api",
        "facebook reels transcript",
        "facebook transcript scraper"
      ],
      "params": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "Facebook video URL (reel, watch, page video, video.php, m./mbasic./web. hosts, fb.watch and share/v|r links) or a numeric video ID.",
          "example": "https://www.facebook.com/reel/1803909017420449",
          "max_length": 2048
        }
      ],
      "sources": [
        "captions",
        "asr"
      ],
      "pricing": [
        {
          "kind": "captions",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        },
        {
          "kind": "asr",
          "credits": 2,
          "per": 2,
          "unit": {
            "id": "media_minute",
            "name": "started minute of media",
            "plural": "started minutes of media",
            "max": 60
          },
          "max": 122,
          "label": "2 credits + 2 per started minute of media"
        }
      ],
      "min_credits": 1,
      "max_credits": 122,
      "errors": [
        "invalid_input",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "no_transcript",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "url": "https://www.facebook.com/reel/1803909017420449/"
        },
        "response": {
          "data": {
            "available_languages": [
              "en-US"
            ],
            "duration_ms": 59861,
            "id": "1803909017420449",
            "is_auto_generated": false,
            "language": "en-US",
            "platform": "facebook",
            "segments": [
              {
                "end_ms": 4000,
                "start_ms": 33,
                "text": "But the standard that we should continuously aspire to exceed,"
              },
              {
                "end_ms": 7166,
                "start_ms": 4000,
                "text": "which is why we are bringing that intensity, that discipline, that"
              },
              {
                "end_ms": 11400,
                "start_ms": 7166,
                "text": "engineering ownership back to NASA, because great nations do not remain great"
              }
            ],
            "source": "captions",
            "text": "But the standard that we should continuously aspire to exceed, which is why we are bringing that intensity, that discipline, that engineering ownership back to NASA, because great nations do not remain great by being caretakers of history, but by continuously making history. We h…",
            "url": "https://www.facebook.com/reel/1803909017420449/"
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "facebook",
            "source": "captions",
            "is_cached": false,
            "credits_charged": 1,
            "latency_ms": 1517,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": {
              "kind": "video",
              "id": "1803909017420449",
              "url": "https://www.facebook.com/reel/1803909017420449/"
            },
            "credits_remaining": 9999,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "reel with uploaded captions",
          "params": {
            "url": "https://www.facebook.com/reel/1803909017420449"
          },
          "tags": [
            "short",
            "captions"
          ]
        },
        {
          "name": "page video with uploaded captions (long)",
          "params": {
            "url": "https://www.facebook.com/NASA/videos/1408202287429345/"
          },
          "tags": [
            "long",
            "captions"
          ]
        },
        {
          "name": "watch url with auto-generated captions",
          "params": {
            "url": "https://www.facebook.com/watch/?v=1755978666533018"
          },
          "tags": [
            "auto_captions"
          ]
        },
        {
          "name": "share link resolves",
          "params": {
            "url": "https://www.facebook.com/share/r/1CfnJ9UQoT/"
          },
          "tags": [
            "share_link"
          ]
        },
        {
          "name": "login-required video",
          "params": {
            "url": "https://www.facebook.com/video.php?v=637842556329505"
          },
          "tags": [
            "private"
          ]
        },
        {
          "name": "garbage url",
          "params": {
            "url": "https://www.facebook.com/NASA/posts/123456789"
          },
          "tags": [
            "invalid"
          ]
        }
      ],
      "stats": {
        "requests": 26,
        "success_rate": 0.6538,
        "p50_ms": 1517,
        "p95_ms": 2437
      },
      "lifecycle": null,
      "identity": "url",
      "accepts": [
        "video"
      ],
      "accepted_params": [
        "url",
        "max_age",
        "fields"
      ]
    },
    {
      "id": "instagram.post",
      "platform": "instagram",
      "name": "post",
      "resource": "post",
      "list": null,
      "path": "/v1/instagram/post",
      "method": "GET",
      "operation_id": "instagram.post.get",
      "status": "beta",
      "title": "Instagram Post API",
      "summary": "Instagram post, reel or carousel with media and metrics",
      "description": "Returns a public Instagram post, reel or carousel: the caption with its hashtags and mentions, the author and any collaborators, the publish time, every carousel item's image or video URL with its size, duration and alt text, and the like, comment and (for single videos) view counts. Accepts post, reel, tv, instagr.am and share links or a raw shortcode.",
      "use_cases": [
        "Track engagement on influencer and brand posts",
        "Monitor competitors' reels and campaigns",
        "Collect captions, hashtags and tags for social listening",
        "Archive post media and metadata",
        "Verify sponsored-post deliverables"
      ],
      "notes": [
        "Media URLs are Instagram CDN links that expire: media[].expires_at says when; download what you need before then.",
        "view_count is set for single videos and reels only (Instagram shows no view count for photos and carousels). like_count and view_count are null when the owner hides them.",
        "Instagram does not expose share or save counts, pinned state, sponsorship labels or the caption's language to logged-out visitors: those fields are null.",
        "Private, age-restricted and deleted posts return private_or_unavailable or not_found and are not charged."
      ],
      "faq": [
        {
          "q": "Does it return every image of a carousel?",
          "a": "Yes: media[] has one entry per carousel item, in display order, each with its own URL, size and, for videos, duration."
        },
        {
          "q": "Who is in mentions?",
          "a": "Accounts @mentioned in the caption, then accounts tagged in the media, without duplicates. Collaborators of a collab post are in coauthors."
        }
      ],
      "keywords": [
        "instagram post api",
        "instagram reel scraper",
        "instagram carousel downloader api",
        "instagram likes and comments api"
      ],
      "params": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "Instagram post, reel or carousel URL (instagram.com/p/…, /reel/…, /reels/…, /tv/…, /<username>/p/…, m.instagram.com, instagr.am, instagram.com/share/… links) or a raw shortcode. Tracking params and img_index are ignored: the whole post is returned.",
          "example": "https://www.instagram.com/reel/De1S3KpCXf0/",
          "max_length": 2048
        }
      ],
      "sources": [
        "page"
      ],
      "pricing": [
        {
          "kind": "page",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        }
      ],
      "min_credits": 1,
      "max_credits": 1,
      "errors": [
        "invalid_input",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "url": "https://www.instagram.com/p/De1S3KpCXf0/"
        },
        "response": {
          "data": {
            "approximate_fields": [],
            "author": {
              "avatar_url": "https://scontent-atl3-2.cdninstagram.com/v/t51.82787-19/683576066_18653628823019133_9051036240972105113_n.jpg?stp=dst-jpg_s150x150_tt6&_nc_cat=1&ccb=SCRUBBED&_nc_sid=SCRUBBED&efg=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=24&_nc_ht=scontent-atl3-2.cdninstagram.com&_nc_gid=S…",
              "display_name": "National Geographic",
              "id": "787132",
              "is_verified": true,
              "url": "https://www.instagram.com/natgeo/",
              "username": "natgeo"
            },
            "coauthors": [
              {
                "avatar_url": "https://scontent-atl3-1.cdninstagram.com/v/t51.2885-19/293493364_162817252949028_8275460966213229030_n.jpg?stp=dst-jpg_s150x150_tt6&_nc_cat=108&ccb=SCRUBBED&_nc_sid=SCRUBBED&efg=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=24&_nc_ht=scontent-atl3-1.cdninstagram.com&_nc_ss=7b6…",
                "display_name": "National Geographic Society",
                "id": "4757225830",
                "is_verified": true,
                "url": "https://www.instagram.com/natgeosociety/",
                "username": "natgeosociety"
              },
              {
                "avatar_url": "https://scontent-atl3-2.cdninstagram.com/v/t51.2885-19/11419091_505296402966021_797003013_a.jpg?_nc_cat=101&ccb=SCRUBBED&_nc_sid=SCRUBBED&efg=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=24&_nc_ht=scontent-atl3-2.cdninstagram.com&_nc_ss=7b689&oh=SCRUBBED&oe=SCRUBBED",
                "display_name": "Joel Sartore- Photo Ark",
                "id": "225429667",
                "is_verified": true,
                "url": "https://www.instagram.com/joelsartore/",
                "username": "joelsartore"
              },
              {
                "avatar_url": "https://scontent-atl3-2.cdninstagram.com/v/t51.82787-19/681945023_18592998970017301_7811445524976503069_n.jpg?stp=dst-jpg_s150x150_tt6&_nc_cat=1&ccb=SCRUBBED&_nc_sid=SCRUBBED&efg=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=24&_nc_ht=scontent-atl3-2.cdninstagram.com&_nc_gid=S…",
                "display_name": "National Geographic Animals",
                "id": "1029649300",
                "is_verified": true,
                "url": "https://www.instagram.com/natgeoanimals/",
                "username": "natgeoanimals"
              }
            ],
            "duration_ms": 23744,
            "hashtags": [
              "PhotoArk"
            ],
            "id": "De1S3KpCXf0",
            "is_nsfw": null,
            "is_pinned": null,
            "is_sponsored": null,
            "language": null,
            "media": [
              {
                "alt_text": "Video by National Geographic on October 23, 2026. May be an image of snail and text.",
                "duration_ms": 23744,
                "expires_at": null,
                "height": 1920,
                "thumbnail_url": "https://scontent-atl3-2.cdninstagram.com/v/t51.82787-15/862543567_18711062458019133_8845814561108513720_n.jpg?stp=dst-jpg_e15_tt6&_nc_cat=105&ig_cache_key=NDAxNDE5NzU5ODcwNDk5MDE5NjE4NzExMDYyNDUyMDE5MTMz.3-ccb7-5&ccb=SCRUBBED&_nc_sid=SCRUBBED&efg=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=…",
                "type": "video",
                "url": "https://scontent-atl3-1.cdninstagram.com/o1/v/t2/f2/m86/AQO0Zc3E_eoYHXxPOVvPDNzjiHnCGoREl8vxeookIy4831a0VLAN6T9CITX0dyH2cQS6Dy0fs5SGxYC42fWpG2JeAvlnP9yrsgmHUk8.mp4?_nc_cat=106&_nc_sid=SCRUBBED&_nc_ht=scontent-atl3-1.cdninstagram.com&_nc_ohc=SCRUBBED&efg=SCRUBBED&ccb=SCRUBBED&vs=S…",
                "width": 1080
              }
            ],
            "mentions": [
              "joelsartore",
              "natgeoanimals",
              "natgeosociety"
            ],
            "metrics": {
              "comment_count": 238,
              "like_count": 68253,
              "quote_count": null,
              "repost_count": null,
              "save_count": null,
              "share_count": null,
              "view_count": 759188
            },
            "platform": "instagram",
            "platform_type": "reel",
            "published_at": "2026-10-23T10:01:35Z",
            "text": "Warning—looking at nudibranchs may cause excessive awe. @joelsartore loves these little creatures, and so do we. 💛🧡🩷\n\nWe’re celebrating 20 years of the National Geographic #PhotoArk, an audacious project documenting the world's incredible biodiversity and highlighting the importa…",
            "thumbnail_url": "https://scontent-atl3-2.cdninstagram.com/v/t51.82787-15/862543567_18711062458019133_8845814561108513720_n.jpg?stp=dst-jpg_e35_s640x640_tt6&_nc_cat=105&ccb=SCRUBBED&_nc_sid=SCRUBBED&efg=SCRUBBED&_nc_ohc=SCRUBBED&_nc_oc=SCRUBBED&_nc_zt=23&_nc_ht=scontent-atl3-2.cdninstagram.com&_nc…",
            "title": null,
            "type": "video",
            "url": "https://www.instagram.com/reel/De1S3KpCXf0/"
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "instagram",
            "source": "page",
            "is_cached": false,
            "credits_charged": 1,
            "latency_ms": 0,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": {
              "kind": "post",
              "id": "De1S3KpCXf0",
              "url": "https://www.instagram.com/reel/De1S3KpCXf0/"
            },
            "credits_remaining": 9999,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "reel with collaborators (natgeo)",
          "params": {
            "url": "https://www.instagram.com/reel/De1S3KpCXf0/"
          },
          "tags": [
            "reel",
            "collab",
            "english"
          ]
        },
        {
          "name": "carousel of photos and videos (garyvee)",
          "params": {
            "url": "https://www.instagram.com/p/DexcKhDDno3/?img_index=2"
          },
          "tags": [
            "carousel",
            "mixed",
            "img_index"
          ]
        },
        {
          "name": "single photo (natgeo)",
          "params": {
            "url": "https://www.instagram.com/p/DeuOR-CjKE9/"
          },
          "tags": [
            "image"
          ]
        },
        {
          "name": "long reel from 2022 (garyvee)",
          "params": {
            "url": "https://www.instagram.com/reel/ChdZ_3UALgB/"
          },
          "tags": [
            "reel",
            "long",
            "old"
          ]
        },
        {
          "name": "share link (resolves to DI-yIpNu17J)",
          "params": {
            "url": "https://www.instagram.com/share/reel/_oDaXMUlS/"
          },
          "tags": [
            "share_link",
            "reel",
            "non_english"
          ]
        },
        {
          "name": "deleted / nonexistent post",
          "params": {
            "url": "https://www.instagram.com/reel/De1S3KpCXf1/"
          },
          "tags": [
            "deleted",
            "expected_error"
          ]
        }
      ],
      "stats": null,
      "lifecycle": null,
      "identity": "url",
      "accepts": [
        "post",
        "video"
      ],
      "accepted_params": [
        "url",
        "max_age",
        "fields"
      ]
    },
    {
      "id": "instagram.transcript",
      "platform": "instagram",
      "name": "transcript",
      "resource": "transcript",
      "list": null,
      "path": "/v1/instagram/transcript",
      "method": "GET",
      "operation_id": "instagram.transcript.get",
      "status": "beta",
      "title": "Instagram Reels Transcript API",
      "summary": "Instagram reel / video transcript",
      "description": "Returns the timed transcript of a public Instagram reel or video post. Instagram rarely exposes caption tracks to logged-out clients, so most transcripts are speech-to-text on the post's audio. Accepts reel, post, tv, instagr.am and share links or a raw shortcode; for carousels, img_index selects the item.",
      "use_cases": [
        "Turn reels into searchable text",
        "Monitor influencer and competitor content",
        "Repurpose reels into captions, blogs and posts",
        "Feed reels into LLM analysis"
      ],
      "notes": [
        "Most results come from speech-to-text (meta.source = asr) and are priced per minute.",
        "Image posts return invalid_input; videos without speech return no_transcript."
      ],
      "faq": [],
      "keywords": [
        "instagram reels transcript",
        "instagram video to text api",
        "instagram transcript scraper"
      ],
      "params": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "Instagram reel/post URL (instagram.com/reel/…, /reels/…, /p/…, /tv/…, instagr.am, instagram.com/share/… links) or a raw shortcode. For carousel posts, ?img_index=N selects the item; default is the first video.",
          "example": "https://www.instagram.com/reel/De1S3KpCXf0/",
          "max_length": 2048
        }
      ],
      "sources": [
        "captions",
        "asr"
      ],
      "pricing": [
        {
          "kind": "captions",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        },
        {
          "kind": "asr",
          "credits": 2,
          "per": 2,
          "unit": {
            "id": "media_minute",
            "name": "started minute of media",
            "plural": "started minutes of media",
            "max": 60
          },
          "max": 122,
          "label": "2 credits + 2 per started minute of media"
        }
      ],
      "min_credits": 1,
      "max_credits": 122,
      "errors": [
        "invalid_input",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "no_transcript",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "url": "https://www.instagram.com/p/De1S3KpCXf0/"
        },
        "response": {
          "data": {
            "available_languages": [],
            "duration_ms": 23745,
            "id": "De1S3KpCXf0",
            "is_auto_generated": true,
            "language": "en",
            "platform": "instagram",
            "segments": [
              {
                "end_ms": 3580,
                "start_ms": 0,
                "text": "You are looking at nudibranchs. You ever seen anything that crazy looking?"
              },
              {
                "end_ms": 9220,
                "start_ms": 3800,
                "text": "Every color in the rainbow. These are marine snails basically that have lost their shells"
              },
              {
                "end_ms": 13480,
                "start_ms": 9220,
                "text": "over time and developed these elaborate colors. Usually brightly colored animals are toxic."
              }
            ],
            "source": "asr",
            "text": "You are looking at nudibranchs. You ever seen anything that crazy looking? Every color in the rainbow. These are marine snails basically that have lost their shells over time and developed these elaborate colors. Usually brightly colored animals are toxic. Some of these are, some…",
            "url": "https://www.instagram.com/reel/De1S3KpCXf0/"
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "instagram",
            "source": "asr",
            "is_cached": false,
            "credits_charged": 4,
            "latency_ms": 4125,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": {
              "kind": "video",
              "id": "De1S3KpCXf0",
              "url": "https://www.instagram.com/reel/De1S3KpCXf0/"
            },
            "credits_remaining": 9996,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "reel en short (natgeo, 24s)",
          "params": {
            "url": "https://www.instagram.com/reel/De1S3KpCXf0/"
          },
          "tags": [
            "short",
            "reel",
            "english"
          ]
        },
        {
          "name": "silent audio track, ASR skipped (bbcnews, 35s)",
          "params": {
            "url": "https://www.instagram.com/reel/De6q2K4lYUd/"
          },
          "tags": [
            "no_speech",
            "silent",
            "expected_error"
          ]
        },
        {
          "name": "username-prefixed URL (natgeo, 10s)",
          "params": {
            "url": "https://www.instagram.com/natgeo/reel/De4MvD0gWju/"
          },
          "tags": [
            "short",
            "user_path"
          ]
        },
        {
          "name": "german (tagesschau, 18s)",
          "params": {
            "url": "https://www.instagram.com/reel/De3-c3llKm7/"
          },
          "tags": [
            "non_english",
            "german"
          ]
        },
        {
          "name": "share link reel (resolves to DI-yIpNu17J)",
          "params": {
            "url": "https://www.instagram.com/share/reel/_oDaXMUlS/"
          },
          "tags": [
            "share_link"
          ]
        },
        {
          "name": "image post",
          "params": {
            "url": "https://www.instagram.com/p/DeuOR-CjKE9/"
          },
          "tags": [
            "image",
            "expected_error"
          ]
        },
        {
          "name": "deleted / nonexistent post",
          "params": {
            "url": "https://www.instagram.com/reel/De1S3KpCXf1/"
          },
          "tags": [
            "deleted",
            "expected_error"
          ]
        }
      ],
      "stats": {
        "requests": 53,
        "success_rate": 0.9811,
        "p50_ms": 4125,
        "p95_ms": 9203
      },
      "lifecycle": null,
      "identity": "url",
      "accepts": [
        "video",
        "post"
      ],
      "accepted_params": [
        "url",
        "max_age",
        "fields"
      ]
    },
    {
      "id": "tiktok.profile",
      "platform": "tiktok",
      "name": "profile",
      "resource": "profile",
      "list": null,
      "path": "/v1/tiktok/profile",
      "method": "GET",
      "operation_id": "tiktok.profile.get",
      "status": "beta",
      "title": "TikTok Profile API",
      "summary": "Public TikTok profile and follower counts",
      "description": "Returns a public TikTok account: user ID, username, display name, bio, bio link, avatar, verification badge, whether the account is private, its business category, join date and exact follower, following, video and total like counts. Accepts a username with or without the @, a tiktok.com profile URL, or a numeric user ID.",
      "use_cases": [
        "Track creator growth over time",
        "Vet influencers before a campaign",
        "Enrich CRM or lead lists with TikTok accounts",
        "Resolve a post author's ID to a full profile"
      ],
      "notes": [
        "Private accounts return their profile with is_private true; their videos are not public.",
        "type is organization for accounts TikTok marks as organizations and unknown otherwise: TikTok does not label people.",
        "category is set only for business accounts. location, country_code, banner_url and metrics.total_view_count are always null: public TikTok profiles do not show them.",
        "When the profile page is unavailable the answer comes from TikTok's embed profile: follower and like counts are then rounded (listed in approximate_fields), and the join date, bio link, category, type and video count are unknown.",
        "Share links (vm.tiktok.com) are not accepted: pass the username or the profile URL."
      ],
      "faq": [],
      "keywords": [
        "tiktok profile api",
        "tiktok follower count api",
        "tiktok user info api",
        "tiktok profile scraper"
      ],
      "params": [
        {
          "name": "handle",
          "type": "string",
          "required": true,
          "description": "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.",
          "example": "nasa",
          "max_length": 2048
        }
      ],
      "sources": [
        "page"
      ],
      "pricing": [
        {
          "kind": "page",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        }
      ],
      "min_credits": 1,
      "max_credits": 1,
      "errors": [
        "invalid_input",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "handle": "nasa"
        },
        "response": {
          "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=SCRUBBED&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",
            "type": "organization",
            "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"
          }
        },
        "truncated": false
      },
      "canaries": [
        {
          "name": "NASA by username",
          "params": {
            "handle": "nasa"
          },
          "tags": [
            "organization",
            "handle"
          ]
        },
        {
          "name": "Creator by @username",
          "params": {
            "handle": "@khaby.lame"
          },
          "tags": [
            "creator",
            "at_handle"
          ]
        },
        {
          "name": "Business account by profile URL",
          "params": {
            "handle": "https://www.tiktok.com/@duolingo"
          },
          "tags": [
            "business",
            "url"
          ]
        },
        {
          "name": "By numeric user ID",
          "params": {
            "handle": "6737188750352401410"
          },
          "tags": [
            "raw_id"
          ]
        },
        {
          "name": "Account that does not exist",
          "params": {
            "handle": "zzq9x81nouser"
          },
          "tags": [
            "deleted"
          ]
        },
        {
          "name": "Video link is refused",
          "params": {
            "handle": "https://www.tiktok.com/@nasa/video/7665075736742530317"
          },
          "tags": [
            "invalid"
          ]
        }
      ],
      "stats": null,
      "lifecycle": null,
      "identity": "handle",
      "accepts": [
        "profile"
      ],
      "accepted_params": [
        "handle",
        "max_age",
        "fields"
      ]
    },
    {
      "id": "tiktok.transcript",
      "platform": "tiktok",
      "name": "transcript",
      "resource": "transcript",
      "list": null,
      "path": "/v1/tiktok/transcript",
      "method": "GET",
      "operation_id": "tiktok.transcript.get",
      "status": "beta",
      "title": "TikTok Transcript API",
      "summary": "TikTok video transcript",
      "description": "Returns the timed transcript of a public TikTok video: creator-edited captions, TikTok's auto-captions, or TikTok's own translation when you ask for another language with lang. Videos without any caption track are transcribed from their audio with speech-to-text. Accepts tiktok.com video, mobile, embed and vm./vt./t/ share links, or a numeric video ID.",
      "use_cases": [
        "Analyze what creators say at scale",
        "Track brand and product mentions in videos",
        "Feed short-form video into LLM pipelines",
        "Caption and translate TikTok content"
      ],
      "notes": [
        "Photo posts have no audio track and return no_transcript.",
        "Videos with licensed music and no speech may transcribe the song lyrics."
      ],
      "faq": [],
      "keywords": [
        "tiktok captions api",
        "tiktok subtitles api",
        "tiktok transcript scraper"
      ],
      "params": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "TikTok video URL (tiktok.com/@user/video/<id>, m.tiktok.com/v/<id>.html, vm.tiktok.com/<code>, vt.tiktok.com/<code>, tiktok.com/t/<code>, embed URLs) or a numeric video ID.",
          "example": "https://www.tiktok.com/@scout2015/video/6718335390845095173",
          "max_length": 2048
        },
        {
          "name": "lang",
          "type": "string",
          "required": false,
          "description": "Preferred language: a BCP-47 tag such as en or pt-BR (case-insensitive; _ is read as -). Selects the caption track (TikTok also offers its own translations); falls back to the video's original language, then any track. TikTok's three-letter codes (eng-US) work too.",
          "example": "en",
          "format": "bcp47",
          "vocabulary": "lang"
        }
      ],
      "sources": [
        "captions",
        "asr"
      ],
      "pricing": [
        {
          "kind": "captions",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        },
        {
          "kind": "asr",
          "credits": 2,
          "per": 2,
          "unit": {
            "id": "media_minute",
            "name": "started minute of media",
            "plural": "started minutes of media",
            "max": 60
          },
          "max": 122,
          "label": "2 credits + 2 per started minute of media"
        }
      ],
      "min_credits": 1,
      "max_credits": 122,
      "errors": [
        "invalid_input",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "no_transcript",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "url": "7673108027645414678"
        },
        "response": {
          "data": {
            "available_languages": [
              "en"
            ],
            "duration_ms": 495000,
            "id": "7673108027645414678",
            "is_auto_generated": false,
            "language": "en",
            "platform": "tiktok",
            "segments": [
              {
                "end_ms": 4820,
                "start_ms": 100,
                "text": "Let me show you how to make a Japanese cream stew!"
              },
              {
                "end_ms": 8380,
                "start_ms": 4821,
                "text": "I think there is a way to definitely make it from scratch,"
              },
              {
                "end_ms": 13120,
                "start_ms": 8381,
                "text": "but most Japanese people buy the stock for the cream stew."
              }
            ],
            "source": "captions",
            "text": "Let me show you how to make a Japanese cream stew! I think there is a way to definitely make it from scratch, but most Japanese people buy the stock for the cream stew. If you go to a Japanese supermarket, you can find this next to or near where the curry stock cubes are. This is…",
            "url": "https://www.tiktok.com/@niinoclock/video/7673108027645414678"
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "tiktok",
            "source": "captions",
            "is_cached": false,
            "credits_charged": 1,
            "latency_ms": 668,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": {
              "kind": "video",
              "id": "7673108027645414678",
              "url": "https://www.tiktok.com/@niinoclock/video/7673108027645414678"
            },
            "credits_remaining": 9999,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "en creator captions",
          "params": {
            "url": "https://www.tiktok.com/@hankgreen1/video/7047596209028074758"
          },
          "tags": [
            "creator_captions"
          ]
        },
        {
          "name": "fr auto-captions",
          "params": {
            "url": "https://www.tiktok.com/@elan.media_/video/7663447693913509152"
          },
          "tags": [
            "non_english",
            "auto_captions"
          ]
        },
        {
          "name": "lang=en translation of a fr video",
          "params": {
            "lang": "en",
            "url": "https://www.tiktok.com/@elan.media_/video/7663447693913509152"
          },
          "tags": [
            "lang",
            "translation"
          ]
        },
        {
          "name": "long video, raw id",
          "params": {
            "url": "7673108027645414678"
          },
          "tags": [
            "long",
            "raw_id"
          ]
        },
        {
          "name": "es creator captions via embed URL",
          "params": {
            "url": "https://www.tiktok.com/embed/v2/7691047892483656974"
          },
          "tags": [
            "embed_url",
            "non_english"
          ]
        },
        {
          "name": "never existed",
          "params": {
            "url": "7000000000000000001"
          },
          "tags": [
            "deleted"
          ]
        },
        {
          "name": "invalid input",
          "params": {
            "url": "https://www.tiktok.com/@hankgreen1"
          },
          "tags": [
            "invalid"
          ]
        }
      ],
      "stats": {
        "requests": 112,
        "success_rate": 0.9911,
        "p50_ms": 668,
        "p95_ms": 2705
      },
      "lifecycle": null,
      "identity": "url",
      "accepts": [
        "video",
        "post"
      ],
      "accepted_params": [
        "url",
        "lang",
        "max_age",
        "fields"
      ]
    },
    {
      "id": "youtube.comments",
      "platform": "youtube",
      "name": "comments",
      "resource": "comment",
      "list": {
        "page_max": 20,
        "cursor_ttl_s": 3600,
        "order": "newest first"
      },
      "path": "/v1/youtube/comments",
      "method": "GET",
      "operation_id": "youtube.comments.list",
      "status": "beta",
      "title": "YouTube Comments API",
      "summary": "Top-level comments of a YouTube video",
      "description": "Returns the public top-level comments of a YouTube video, short or live replay, a page at a time: text, author handle and channel, posting time, likes, reply count, and whether the comment is pinned, hearted, edited or written by the creator. Sort newest first (the default, which also supports since and until) or by YouTube's Top ranking, and follow page.next_cursor for more. Accepts every YouTube URL form or a bare 11-character video ID.",
      "use_cases": [
        "Track audience sentiment on your videos and competitors'",
        "Monitor new comments since a date",
        "Find questions and feedback to answer",
        "Build datasets of public comments for research"
      ],
      "notes": [
        "YouTube shows comment times as relative labels (\"3 weeks ago\"): published_at is the latest time the label allows and is listed in approximate_fields, so since and until are approximate to the label's unit.",
        "Like and reply counts above a thousand are rounded by YouTube (\"4.8K\") and listed in approximate_fields.",
        "Comments show the author's @handle; author.display_name is null.",
        "The newest order includes comments YouTube's Top ranking holds back as potential spam.",
        "Videos with comments turned off and private videos return private_or_unavailable; deleted videos return not_found.",
        "Replies are not included; metrics.reply_count says how many a comment has."
      ],
      "faq": [],
      "keywords": [
        "youtube comments api",
        "youtube comment scraper",
        "youtube comments export",
        "youtube comments json"
      ],
      "params": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "YouTube video URL (watch, youtu.be, shorts, live, embed, m./music. hosts) or an 11-character video ID.",
          "example": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
          "max_length": 2048
        },
        {
          "name": "sort",
          "type": "string",
          "required": false,
          "description": "Order of the results. newest (default): newest first. popular: YouTube's Top comments ranking. Either way the video's pinned comment comes first.",
          "enum": [
            "newest",
            "popular"
          ],
          "vocabulary": "sort"
        },
        {
          "name": "since",
          "type": "string",
          "required": false,
          "description": "Only items at or after this time: RFC 3339 (2026-10-01T12:00:00Z) or a date (2026-10-01, midnight UTC). On newest-first lists, paging stops when it is reached, so later pages are not fetched or charged. Compared with published_at, which YouTube only shows as a relative label (\"2 days ago\"): the walk stops at the first comment whose label is older, so comments up to one label unit older can be included.",
          "example": "2026-10-01",
          "format": "date-time",
          "vocabulary": "since"
        },
        {
          "name": "until",
          "type": "string",
          "required": false,
          "description": "Only items before this time (exclusive): RFC 3339 or a date (midnight UTC).",
          "example": "2026-10-08",
          "format": "date-time",
          "vocabulary": "until"
        }
      ],
      "sources": [
        "api"
      ],
      "pricing": [
        {
          "kind": "api",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        }
      ],
      "min_credits": 1,
      "max_credits": 1,
      "errors": [
        "invalid_input",
        "invalid_cursor",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "url": "jNQXAC9IVRw"
        },
        "response": {
          "data": [
            {
              "approximate_fields": [
                "published_at",
                "metrics.like_count",
                "metrics.reply_count"
              ],
              "author": {
                "avatar_url": "https://yt3.ggpht.com/i8RqA9tYqbbnOi4mlnYEY7IR3kXAoDqQbM_5Sfzw7OjTubiGsOaTdlrIZmj-I0q0Vup68eqs=s88-c-k-c0x00ffffff-no-rj",
                "display_name": null,
                "id": "UCC5NfQ6Mf0dq_eEwv4P_hWA",
                "is_verified": true,
                "url": "https://www.youtube.com/channel/UCC5NfQ6Mf0dq_eEwv4P_hWA",
                "username": "SanDiegoZoo"
              },
              "edited_at": null,
              "id": "UgzuC3zzpRZkjc5Qzsd4AaABAg",
              "is_by_post_author": false,
              "is_edited": false,
              "is_liked_by_author": true,
              "is_pinned": true,
              "metrics": {
                "like_count": 4800000,
                "reply_count": 1000
              },
              "parent_id": null,
              "platform": "youtube",
              "post_id": "jNQXAC9IVRw",
              "published_at": "2020-10-26T18:59:19Z",
              "text": "We're so honored that the first ever YouTube video was filmed here!",
              "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw&lc=UgzuC3zzpRZkjc5Qzsd4AaABAg"
            },
            {
              "approximate_fields": [
                "published_at"
              ],
              "author": {
                "avatar_url": "https://yt3.ggpht.com/U9zdJRwdpo6WwsenxMf6AyMyRMBTWoKsyAWCnJXdbPbNKh-YNEWP3AQRgFto91lckyGCBYENnaw=s88-c-k-c0x00ffffff-no-rj",
                "display_name": null,
                "id": "UCjoZE_5A9tO470CK3G6U7dg",
                "is_verified": false,
                "url": "https://www.youtube.com/channel/UCjoZE_5A9tO470CK3G6U7dg",
                "username": "CTwGary"
              },
              "edited_at": null,
              "id": "UgxHbErLhWMUy5GlFnp4AaABAg",
              "is_by_post_author": false,
              "is_edited": false,
              "is_liked_by_author": false,
              "is_pinned": false,
              "metrics": {
                "like_count": 0,
                "reply_count": 0
              },
              "parent_id": null,
              "platform": "youtube",
              "post_id": "jNQXAC9IVRw",
              "published_at": "2026-10-26T18:58:19Z",
              "text": "I can’t believe it, but it’s 21 years old now",
              "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw&lc=UgxHbErLhWMUy5GlFnp4AaABAg"
            },
            {
              "approximate_fields": [
                "published_at"
              ],
              "author": {
                "avatar_url": "https://yt3.ggpht.com/Ar50xvd1O4GCXVttK6DgX0Y0cuhBl2b_HUFQjh-gajKbmS-qjuNAHOT5fw_RX_DS5gzfphkcKAI=s88-c-k-c0x00ffffff-no-rj",
                "display_name": null,
                "id": "UCFgjcRO4y8XFQL7E8_TcLlg",
                "is_verified": false,
                "url": "https://www.youtube.com/channel/UCFgjcRO4y8XFQL7E8_TcLlg",
                "username": "Nihal-poke"
              },
              "edited_at": null,
              "id": "UgzLOxwwIAUe0jUI4RR4AaABAg",
              "is_by_post_author": false,
              "is_edited": false,
              "is_liked_by_author": false,
              "is_pinned": false,
              "metrics": {
                "like_count": 0,
                "reply_count": 0
              },
              "parent_id": null,
              "platform": "youtube",
              "post_id": "jNQXAC9IVRw",
              "published_at": "2026-10-26T18:58:19Z",
              "text": "I watch this video many times 😭",
              "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw&lc=UgzLOxwwIAUe0jUI4RR4AaABAg"
            }
          ],
          "page": {
            "has_more": true,
            "next_cursor": "c1.EXAMPLE_CURSOR",
            "next_cursor_expires_at": "2026-10-26T12:00:00Z",
            "end_reason": null
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "youtube",
            "source": "api",
            "is_cached": false,
            "credits_charged": 1,
            "latency_ms": 0,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": {
              "kind": "video",
              "id": "jNQXAC9IVRw",
              "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
            },
            "credits_remaining": 9999,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "newest comments of the first YouTube video",
          "params": {
            "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw"
          },
          "tags": [
            "newest",
            "watch_url"
          ]
        },
        {
          "name": "top comments via youtu.be",
          "params": {
            "sort": "popular",
            "url": "https://youtu.be/dQw4w9WgXcQ?si=canary"
          },
          "tags": [
            "popular",
            "share_link"
          ]
        },
        {
          "name": "short, limit below the page size",
          "params": {
            "limit": "5",
            "url": "https://www.youtube.com/shorts/3cWm9B_0_kI"
          },
          "tags": [
            "short",
            "limit"
          ]
        },
        {
          "name": "since ends the walk at once (future date)",
          "params": {
            "since": "2099-01-01",
            "url": "jNQXAC9IVRw"
          },
          "tags": [
            "since",
            "empty_page"
          ]
        },
        {
          "name": "comments turned off (made for kids)",
          "params": {
            "url": "https://www.youtube.com/watch?v=XqZsoesa55w"
          },
          "tags": [
            "comments_off",
            "private"
          ]
        },
        {
          "name": "deleted video",
          "params": {
            "url": "Q39EVAstoRM"
          },
          "tags": [
            "deleted",
            "raw_id"
          ]
        },
        {
          "name": "garbage cursor",
          "params": {
            "cursor": "c1.bogus",
            "url": "jNQXAC9IVRw"
          },
          "tags": [
            "cursor"
          ]
        }
      ],
      "stats": null,
      "lifecycle": null,
      "identity": "url",
      "accepts": [
        "video"
      ],
      "accepted_params": [
        "url",
        "sort",
        "since",
        "until",
        "max_age",
        "fields",
        "limit",
        "cursor"
      ]
    },
    {
      "id": "youtube.transcript",
      "platform": "youtube",
      "name": "transcript",
      "resource": "transcript",
      "list": null,
      "path": "/v1/youtube/transcript",
      "method": "GET",
      "operation_id": "youtube.transcript.get",
      "status": "beta",
      "title": "YouTube Transcript API",
      "summary": "YouTube video transcript",
      "description": "Returns the timed transcript of a public YouTube video, short or live replay: the creator's manual captions when they exist, otherwise YouTube's auto-generated track, in the language you ask for. Videos without any caption track are transcribed from their audio with speech-to-text. Accepts every YouTube URL form (watch, youtu.be, shorts, live, embed, music.) or a bare 11-character video ID.",
      "use_cases": [
        "Summarize or search video content with LLMs",
        "Build subtitle and translation tools",
        "Index a channel's back catalog for RAG",
        "Repurpose videos into articles and posts"
      ],
      "notes": [
        "Manual captions are preferred over auto-generated ones; is_auto_generated tells you which you got.",
        "Private, members-only, rental and age-restricted videos return private_or_unavailable."
      ],
      "faq": [],
      "keywords": [
        "youtube captions api",
        "youtube subtitles api",
        "youtube transcript scraper"
      ],
      "params": [
        {
          "name": "url",
          "type": "string",
          "required": true,
          "description": "YouTube video URL (watch, youtu.be, shorts, live, embed, m./music. hosts) or an 11-character video ID.",
          "example": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
          "max_length": 2048
        },
        {
          "name": "lang",
          "type": "string",
          "required": false,
          "description": "Preferred language: a BCP-47 tag such as en or pt-BR (case-insensitive; _ is read as -). Selects the caption track; a manual track is preferred over an auto-generated one. Default: the video's original-language manual track, else its auto-generated track.",
          "example": "en",
          "format": "bcp47",
          "vocabulary": "lang"
        }
      ],
      "sources": [
        "captions",
        "asr"
      ],
      "pricing": [
        {
          "kind": "captions",
          "credits": 1,
          "per": 0,
          "unit": null,
          "max": 1,
          "label": "1 credit"
        },
        {
          "kind": "asr",
          "credits": 2,
          "per": 2,
          "unit": {
            "id": "media_minute",
            "name": "started minute of media",
            "plural": "started minutes of media",
            "max": 60
          },
          "max": 122,
          "label": "2 credits + 2 per started minute of media"
        }
      ],
      "min_credits": 1,
      "max_credits": 122,
      "errors": [
        "invalid_input",
        "unauthorized",
        "forbidden",
        "insufficient_credits",
        "spend_limit_reached",
        "rate_limited",
        "not_found",
        "private_or_unavailable",
        "no_transcript",
        "upstream_blocked",
        "upstream_error",
        "overloaded",
        "internal"
      ],
      "example": {
        "params": {
          "url": "dQw4w9WgXcQ"
        },
        "response": {
          "data": {
            "available_languages": [
              "en",
              "de-DE",
              "ja"
            ],
            "duration_ms": 213000,
            "id": "dQw4w9WgXcQ",
            "is_auto_generated": false,
            "language": "en",
            "platform": "youtube",
            "segments": [
              {
                "end_ms": 3040,
                "start_ms": 1360,
                "text": "[♪♪♪]"
              },
              {
                "end_ms": 21880,
                "start_ms": 18640,
                "text": "♪ We're no strangers to love ♪"
              },
              {
                "end_ms": 26960,
                "start_ms": 22640,
                "text": "♪ You know the rules and so do I ♪"
              }
            ],
            "source": "captions",
            "text": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ ♪ A full commitment's what I'm thinking of ♪ ♪ You wouldn't get this from any other guy ♪ ♪ I just wanna tell you how I'm feeling ♪ ♪ Gotta make you understand ♪ ♪ Never gonna give you up ♪ ♪ Never gonna let …",
            "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
          },
          "meta": {
            "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
            "platform": "youtube",
            "source": "captions",
            "is_cached": false,
            "credits_charged": 1,
            "latency_ms": 511,
            "fetched_at": "2026-10-25T12:00:00Z",
            "input": {
              "kind": "video",
              "id": "dQw4w9WgXcQ",
              "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            },
            "credits_remaining": 9999,
            "client_request_id": null,
            "warnings": [],
            "mode": "live"
          }
        },
        "truncated": true
      },
      "canaries": [
        {
          "name": "manual en captions (watch url)",
          "params": {
            "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
          },
          "tags": [
            "short",
            "manual"
          ]
        },
        {
          "name": "first youtube video via youtu.be",
          "params": {
            "url": "https://youtu.be/jNQXAC9IVRw"
          },
          "tags": [
            "short",
            "share_link"
          ]
        },
        {
          "name": "long lecture, auto-generated only (raw id)",
          "params": {
            "url": "kCc8FmEb1nY"
          },
          "tags": [
            "long",
            "auto_generated",
            "raw_id"
          ]
        },
        {
          "name": "TED talk lang selection",
          "params": {
            "lang": "pt-BR",
            "url": "https://www.youtube.com/watch?v=iG9CE55wbtY"
          },
          "tags": [
            "multi_language",
            "lang",
            "non_english"
          ]
        },
        {
          "name": "requested language not offered",
          "params": {
            "lang": "fr",
            "url": "jNQXAC9IVRw"
          },
          "tags": [
            "no_transcript",
            "lang"
          ]
        },
        {
          "name": "private video",
          "params": {
            "url": "yZIXLfi8CZQ"
          },
          "tags": [
            "private"
          ]
        },
        {
          "name": "video that never existed",
          "params": {
            "url": "Q39EVAstoRM"
          },
          "tags": [
            "deleted"
          ]
        }
      ],
      "stats": {
        "requests": 29,
        "success_rate": 0.9655,
        "p50_ms": 511,
        "p95_ms": 989
      },
      "lifecycle": null,
      "identity": "url",
      "accepts": [
        "video"
      ],
      "accepted_params": [
        "url",
        "lang",
        "max_age",
        "fields"
      ]
    }
  ],
  "errors": [
    {
      "code": "invalid_input",
      "status": 400,
      "retryable": false,
      "upstream_failure": false,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The request parameters are missing or malformed, or the URL is not a supported link for this endpoint.",
      "remediation": "Fix the parameter named in param (the message says how); for a link of the wrong kind, call the endpoint the message names.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#invalid_input",
      "details": [
        {
          "name": "suggestion",
          "type": "string",
          "doc": "For an unknown parameter: the accepted parameter it most likely means; absent when nothing is close."
        },
        {
          "name": "accepted_params",
          "type": "string[]",
          "doc": "For an unknown parameter: every query parameter this endpoint accepts."
        },
        {
          "name": "url_kind",
          "type": "string",
          "doc": "For a link of the wrong kind: what the link points to (profile, video, post, …)."
        },
        {
          "name": "url_platform",
          "type": "string",
          "doc": "For a link of the wrong kind: the platform the link belongs to."
        },
        {
          "name": "accepted_url_kinds",
          "type": "string[]",
          "doc": "For a link of the wrong kind: the kinds of link this endpoint takes."
        },
        {
          "name": "endpoints",
          "type": "string[]",
          "doc": "For a link of the wrong kind: IDs of the endpoints that take it; empty when none does yet."
        },
        {
          "name": "fields",
          "type": "string[]",
          "doc": "For an unknown fields path: every path fields accepts on this endpoint."
        }
      ]
    },
    {
      "code": "invalid_cursor",
      "status": 400,
      "retryable": false,
      "upstream_failure": false,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The cursor is malformed, expired, belongs to another endpoint, or the other parameters differ from the request that issued it.",
      "remediation": "Repeat the original request without cursor to start the list again.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#invalid_cursor",
      "details": [
        {
          "name": "reason",
          "type": "string",
          "doc": "Why: malformed, expired, wrong_endpoint, params_changed or pin_lost (the upstream exit a cursor-bound list was pinned to is gone; new values may be added)."
        }
      ]
    },
    {
      "code": "unauthorized",
      "status": 401,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The x-api-key header is missing, unknown or revoked (or, on the dashboard's routes, the session cookie is missing or expired).",
      "remediation": "Send a valid API key in the x-api-key header; without one, get a key at details.signup_url (AI agents: POST details.agent_signup_url).",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#unauthorized",
      "details": [
        {
          "name": "signup_url",
          "type": "string",
          "doc": "Where a human gets an API key (self-serve signup)."
        },
        {
          "name": "agent_signup_url",
          "type": "string",
          "doc": "POST an email address here to get a test key at once and a live key once the human confirms."
        }
      ]
    },
    {
      "code": "forbidden",
      "status": 403,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The API key is valid but may not make this request: its account is suspended, its scopes do not include the route, it is waiting for its owner's email confirmation, it is past its expiry, or the request came from an IP its allowlist does not include.",
      "remediation": "Do what details.reason says: if suspended, contact support; otherwise use a key whose scopes cover the route (details.required_scope), have the owner confirm the emailed link, create a new key, or call from an allowed IP.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#forbidden",
      "details": [
        {
          "name": "reason",
          "type": "string",
          "doc": "Why: suspended (an operator suspended the account), scope (the key lacks details.required_scope), key_pending (an agent signup's live key before its owner confirms), key_expired, ip_not_allowed, test_mode (a test key asked for something live, such as a live key) or csrf (a dashboard request from another site). New values may be added."
        },
        {
          "name": "required_scope",
          "type": "string",
          "doc": "For reason scope: the scope the route needs (endpoints, jobs, account:read or admin)."
        }
      ]
    },
    {
      "code": "insufficient_credits",
      "status": 402,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The account does not have enough credits for this request.",
      "remediation": "Buy a credit pack, then repeat the request; do not retry automatically.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#insufficient_credits",
      "details": [
        {
          "name": "credits_required",
          "type": "integer",
          "doc": "Credits this request needed at the point it stopped (to start, or to finish a longer item)."
        },
        {
          "name": "credits_available",
          "type": "integer",
          "doc": "Credits the account could spend at that moment (in-flight requests hold part of the balance)."
        },
        {
          "name": "max_credits",
          "type": "integer",
          "doc": "For a job's item: the job's max_credits, when that cap (not the balance) stopped the item."
        }
      ]
    },
    {
      "code": "spend_limit_reached",
      "status": 402,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The API key reached its spend limit for the current period; the account may still have credits.",
      "remediation": "Wait until details.resets_at or raise the key's spend limit (PATCH /v1/api_keys/{id}); do not retry automatically.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#spend_limit_reached",
      "details": [
        {
          "name": "api_key_id",
          "type": "string",
          "doc": "The key whose limit was reached."
        },
        {
          "name": "spend_limit",
          "type": "integer",
          "doc": "The limit: most credits the key may spend per interval."
        },
        {
          "name": "interval",
          "type": "string",
          "doc": "day or month (UTC calendar)."
        },
        {
          "name": "used",
          "type": "integer",
          "doc": "Credits the key spent this interval, plus what its requests in flight hold."
        },
        {
          "name": "credits_required",
          "type": "integer",
          "doc": "Credits this request needed."
        },
        {
          "name": "resets_at",
          "type": "string",
          "doc": "When the interval ends and the limit is free again, RFC 3339 UTC."
        }
      ]
    },
    {
      "code": "rate_limited",
      "status": 429,
      "retryable": true,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": true,
      "reserved": false,
      "description": "The account exceeded its runaway-protection ceiling: plans do not limit request rate, but a high per-account safety ceiling stops broken client loops. Support raises it on request.",
      "remediation": "Wait for the number of seconds in the retry-after header, then retry; ask support to raise the ceiling if your sustained traffic needs more.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#rate_limited",
      "details": [
        {
          "name": "retry_after",
          "type": "integer",
          "doc": "Seconds to wait before retrying; equals the retry-after header."
        }
      ]
    },
    {
      "code": "method_not_allowed",
      "status": 405,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The endpoint exists but does not accept this HTTP method.",
      "remediation": "Use the method in the allow header (GET for every data endpoint).",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#method_not_allowed",
      "details": []
    },
    {
      "code": "endpoint_retired",
      "status": 410,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The endpoint passed its sunset date and no longer answers.",
      "remediation": "Call the replacement endpoint (details.replacement); the changelog describes the migration.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#endpoint_retired",
      "details": [
        {
          "name": "sunset_at",
          "type": "string",
          "doc": "When the endpoint stopped answering, RFC 3339 UTC."
        },
        {
          "name": "replacement",
          "type": "string",
          "doc": "ID of the endpoint to use instead; absent when there is none."
        }
      ]
    },
    {
      "code": "idempotency_key_reused",
      "status": 422,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "The Idempotency-Key was already used with a different request (another route, or another body).",
      "remediation": "Send a new Idempotency-Key for a different request; reuse a key only to retry the identical request.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#idempotency_key_reused",
      "details": []
    },
    {
      "code": "idempotency_key_in_progress",
      "status": 409,
      "retryable": true,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": true,
      "reserved": false,
      "description": "A request with this Idempotency-Key is still running.",
      "remediation": "Retry after the number of seconds in the retry-after header to get that request's result.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#idempotency_key_in_progress",
      "details": [
        {
          "name": "retry_after",
          "type": "integer",
          "doc": "Seconds to wait before retrying; equals the retry-after header."
        }
      ]
    },
    {
      "code": "job_not_found",
      "status": 404,
      "retryable": false,
      "upstream_failure": false,
      "gateway": true,
      "retry_after": false,
      "reserved": false,
      "description": "No job with this ID exists for the account: unknown, or past its retention.",
      "remediation": "Check the job ID; finished jobs are kept for a limited time.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#job_not_found",
      "details": []
    },
    {
      "code": "not_found",
      "status": 404,
      "retryable": false,
      "upstream_failure": false,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The item does not exist or was deleted.",
      "remediation": "Check the URL or ID; do not retry.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#not_found",
      "details": []
    },
    {
      "code": "private_or_unavailable",
      "status": 403,
      "retryable": false,
      "upstream_failure": false,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The item exists but is private, age- or region-gated, or requires a login to view.",
      "remediation": "Only public items can be fetched; do not retry.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#private_or_unavailable",
      "details": []
    },
    {
      "code": "no_transcript",
      "status": 404,
      "retryable": false,
      "upstream_failure": false,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The media exists but has no transcript: no caption track and no speech to transcribe.",
      "remediation": "Do not retry; try another language with lang if the platform offers one.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#no_transcript",
      "details": []
    },
    {
      "code": "upstream_blocked",
      "status": 503,
      "retryable": true,
      "upstream_failure": true,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The platform refused every attempt.",
      "remediation": "Retry later with backoff; failed requests are free.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#upstream_blocked",
      "details": []
    },
    {
      "code": "upstream_error",
      "status": 502,
      "retryable": true,
      "upstream_failure": true,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "The platform failed or returned something unexpected.",
      "remediation": "Retry with backoff; failed requests are free.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#upstream_error",
      "details": []
    },
    {
      "code": "overloaded",
      "status": 503,
      "retryable": true,
      "upstream_failure": false,
      "gateway": false,
      "retry_after": true,
      "reserved": false,
      "description": "SocialTap was momentarily at capacity and did not start the request: nothing was fetched or charged.",
      "remediation": "Retry after the number of seconds in the retry-after header; failed requests are free.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#overloaded",
      "details": [
        {
          "name": "retry_after",
          "type": "integer",
          "doc": "Seconds to wait before retrying; equals the retry-after header."
        }
      ]
    },
    {
      "code": "internal",
      "status": 500,
      "retryable": true,
      "upstream_failure": true,
      "gateway": false,
      "retry_after": false,
      "reserved": false,
      "description": "Unexpected SocialTap error.",
      "remediation": "Retry with backoff; contact support with the request_id if it persists.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#internal",
      "details": []
    }
  ],
  "headers": [
    {
      "name": "x-request-id",
      "key": "requestId",
      "type": "string",
      "scope": "public",
      "doc": "Unique request ID; include it when contacting support."
    },
    {
      "name": "x-credits-charged",
      "key": "creditsCharged",
      "type": "int",
      "scope": "public",
      "doc": "Credits charged for this request (0 for errors and cache hits)."
    },
    {
      "name": "x-credits-remaining",
      "key": "creditsRemaining",
      "type": "int",
      "scope": "public",
      "doc": "Account balance after this request; for a busy account an estimate that may not yet subtract concurrent requests (about a second of usage), never below the real balance."
    },
    {
      "name": "retry-after",
      "key": "retryAfter",
      "type": "int",
      "scope": "public",
      "doc": "Seconds to wait before retrying; sent only with the codes whose retry_after is set in the error codes list (never on credit errors)."
    },
    {
      "name": "allow",
      "key": "allow",
      "type": "string",
      "scope": "public",
      "doc": "Methods the endpoint accepts (method_not_allowed only)."
    },
    {
      "name": "age",
      "key": "age",
      "type": "int",
      "scope": "public",
      "doc": "Seconds since the data was fetched (cache hits only)."
    },
    {
      "name": "x-client-request-id",
      "key": "clientRequestId",
      "type": "string",
      "scope": "public",
      "doc": "Your x-client-request-id, echoed (also meta.client_request_id)."
    },
    {
      "name": "ratelimit-policy",
      "key": "rateLimitPolicy",
      "type": "string",
      "scope": "public",
      "doc": "The account's runaway-protection ceiling as an IETF RateLimit-Policy field: \"account\";q=<requests>;w=1 (requests per second). Not a plan limit."
    },
    {
      "name": "ratelimit",
      "key": "rateLimit",
      "type": "string",
      "scope": "public",
      "doc": "What is left of the ceiling (IETF RateLimit field): \"account\";r=<remaining>;t=<seconds until it is full again>."
    },
    {
      "name": "deprecation",
      "key": "deprecation",
      "type": "string",
      "scope": "public",
      "doc": "RFC 9745: the endpoint is deprecated, as of this date (@<unix seconds>; may be in the future). Also in meta.warnings."
    },
    {
      "name": "sunset",
      "key": "sunset",
      "type": "string",
      "scope": "public",
      "doc": "RFC 8594: when a deprecated endpoint stops answering (HTTP-date); from then on it answers endpoint_retired."
    },
    {
      "name": "link",
      "key": "link",
      "type": "string",
      "scope": "public",
      "doc": "RFC 8288 links: rel=\"next\" is the next page of a list (while page.has_more), the same request with cursor set; rel=\"deprecation\" is a deprecated endpoint's notice."
    },
    {
      "name": "location",
      "key": "location",
      "type": "string",
      "scope": "public",
      "doc": "On 202 answers: the job's URL (GET it, free, for its status)."
    },
    {
      "name": "preference-applied",
      "key": "preferenceApplied",
      "type": "string",
      "scope": "public",
      "doc": "RFC 7240: respond-async when Prefer: respond-async turned the request into a job (202)."
    },
    {
      "name": "idempotent-replayed",
      "key": "idempotentReplayed",
      "type": "bool",
      "scope": "public",
      "doc": "true when the answer replays an earlier request with the same Idempotency-Key (it shows the resource's current state)."
    },
    {
      "name": "x-api-key",
      "key": "apiKey",
      "type": "string",
      "scope": "client_request",
      "doc": "Your API key."
    },
    {
      "name": "authorization",
      "key": "authorization",
      "type": "string",
      "scope": "client_request",
      "doc": "Bearer <your API key>: the MCP server also takes the key this way (what MCP clients send); the REST API reads x-api-key."
    },
    {
      "name": "x-client-request-id",
      "key": "clientRequestId",
      "type": "string",
      "scope": "client_request",
      "doc": "Your own ID for the request (1–128 printable ASCII characters): echoed back and logged with ours, so support can find a request whose response never reached you."
    },
    {
      "name": "traceparent",
      "key": "traceparent",
      "type": "string",
      "scope": "client_request",
      "doc": "W3C Trace Context of your trace: recorded with the request (its trace ID) so a trace can be matched to ours; SocialTap starts its own trace."
    },
    {
      "name": "idempotency-key",
      "key": "idempotencyKey",
      "type": "string",
      "scope": "client_request",
      "doc": "On any POST: your unique key for the request (1–255 printable ASCII characters; a UUID is ideal), kept 24 hours. Retrying with the same key and body never repeats the work: the answer is the first request's result, in its current state."
    },
    {
      "name": "prefer",
      "key": "prefer",
      "type": "string",
      "scope": "client_request",
      "doc": "RFC 7240. respond-async on any endpoint request runs it as a one-request job: the answer is 202 with the job (Location: its URL) instead of the result."
    },
    {
      "name": "cache-control",
      "key": "cacheControl",
      "type": "string",
      "scope": "client_request",
      "doc": "max-age=N accepts a cached response up to N seconds old, like the max_age parameter (max_age wins when both are sent). Other directives are ignored."
    },
    {
      "name": "webhook-id",
      "key": "webhookId",
      "type": "string",
      "scope": "webhook",
      "doc": "The event ID (evt_…), the same on every retry: deduplicate deliveries on it."
    },
    {
      "name": "webhook-timestamp",
      "key": "webhookTimestamp",
      "type": "int",
      "scope": "webhook",
      "doc": "When this attempt was signed, in Unix seconds: reject deliveries outside your tolerance (replays)."
    },
    {
      "name": "webhook-signature",
      "key": "webhookSignature",
      "type": "string",
      "scope": "webhook",
      "doc": "Space-separated v1,SIGNATURE entries, each the base64 HMAC-SHA256 of webhook-id, webhook-timestamp and the raw body joined by dots, with one active secret (two during a rotation): accept the delivery when one matches."
    }
  ],
  "common_params": [
    {
      "name": "max_age",
      "type": "integer",
      "description": "Accept a cached response up to this many seconds old. Cache hits cost 0 credits. Omit to always fetch fresh. The Cache-Control: max-age=N request header is equivalent; max_age wins when both are sent.",
      "minimum": 0
    },
    {
      "name": "fields",
      "type": "string",
      "description": "Return only these fields of each item: comma-separated paths in the schema's notation (id,text,metrics.view_count,segments[].text; a parent path keeps the whole object). Unknown paths are rejected. Applied after the cache, so it changes neither the price nor cache hits; page and meta are always returned whole."
    }
  ],
  "list_params": [
    {
      "name": "limit",
      "type": "integer",
      "description": "Items per page, 1 to the endpoint's page size (the default and maximum; larger values are reduced). Reduces payload, not price: each page costs the same.",
      "minimum": 1
    },
    {
      "name": "cursor",
      "type": "string",
      "description": "page.next_cursor from the previous response. Send the same other parameters; omit to start from the first page."
    }
  ],
  "page_fields": [
    {
      "path": "has_more",
      "type": "boolean",
      "nullable": false,
      "description": "True when another page exists; then next_cursor is set. An empty page with has_more true is not the end."
    },
    {
      "path": "next_cursor",
      "type": "string",
      "nullable": true,
      "description": "Pass as cursor, with the same other parameters, to get the next page; null at the end."
    },
    {
      "path": "next_cursor_expires_at",
      "type": "string",
      "format": "date-time",
      "nullable": true,
      "description": "When next_cursor stops working, RFC 3339 UTC; null at the end. Don't store cursors."
    },
    {
      "path": "end_reason",
      "type": "string",
      "nullable": true,
      "description": "Why there are no more pages; null while has_more is true. end: the list ended, or reached since/until. platform_limit: the platform stops serving further pages although the list goes on. max_pages: SocialTap's cap on pages in one walk. New values may be added. A short or empty page is not the end: only has_more false is.",
      "enum": [
        "end",
        "platform_limit",
        "max_pages"
      ]
    }
  ],
  "meta_fields": [
    {
      "path": "request_id",
      "type": "string",
      "nullable": false,
      "description": "Unique request ID; include it when contacting support."
    },
    {
      "path": "platform",
      "type": "string",
      "nullable": false,
      "description": "Platform ID of the endpoint (youtube, tiktok, …)."
    },
    {
      "path": "source",
      "type": "string",
      "nullable": false,
      "description": "Which upstream strategy produced the data."
    },
    {
      "path": "is_cached",
      "type": "boolean",
      "nullable": false,
      "description": "True when served from the cache (free); see max_age."
    },
    {
      "path": "credits_charged",
      "type": "integer",
      "nullable": false,
      "description": "Credits charged for this request (0 for errors and cache hits)."
    },
    {
      "path": "latency_ms",
      "type": "integer",
      "nullable": false,
      "description": "Time SocialTap spent on the request, in milliseconds."
    },
    {
      "path": "fetched_at",
      "type": "string",
      "format": "date-time",
      "nullable": false,
      "description": "When the data was fetched from the platform, RFC 3339 UTC (older than the request for cache hits)."
    },
    {
      "path": "input",
      "type": "object",
      "nullable": true,
      "description": "What the request's url or handle resolved to: kind, platform ID and canonical URL, so you can confirm what was fetched; null for searches."
    },
    {
      "path": "input.kind",
      "type": "string",
      "nullable": false,
      "description": "What the input points to (profile, video, post, …). New values may be added; treat unknown values as other."
    },
    {
      "path": "input.id",
      "type": "string",
      "nullable": true,
      "description": "Platform-native ID of the item; null when it is only known after the fetch and the request failed."
    },
    {
      "path": "input.url",
      "type": "string",
      "nullable": true,
      "description": "Canonical URL of the item; null when unknown."
    },
    {
      "path": "credits_remaining",
      "type": "integer",
      "nullable": true,
      "description": "Account balance after this request (the x-credits-remaining header); for a busy account an estimate that may not yet subtract concurrent requests (about a second of usage), never below the real balance; null when SocialTap could not read it."
    },
    {
      "path": "client_request_id",
      "type": "string",
      "nullable": true,
      "description": "Your x-client-request-id, echoed; null when you sent none."
    },
    {
      "path": "warnings",
      "type": "object[]",
      "nullable": false,
      "description": "Things to act on that did not fail the request: a deprecated endpoint, partial data. [] when none."
    },
    {
      "path": "warnings[].code",
      "type": "string",
      "nullable": false,
      "description": "Machine-readable warning code (see the warnings list). New codes may be added."
    },
    {
      "path": "warnings[].message",
      "type": "string",
      "nullable": false,
      "description": "What to do about it."
    },
    {
      "path": "warnings[].doc_url",
      "type": "string",
      "nullable": false,
      "description": "Where to read more (the changelog entry, the endpoint's docs)."
    },
    {
      "path": "mode",
      "type": "string",
      "nullable": true,
      "description": "live, or test for a test key's answer: a recorded response (fixture), free and deterministic; null when SocialTap could not tell.",
      "enum": [
        "live",
        "test"
      ]
    }
  ],
  "error_fields": [
    {
      "path": "code",
      "type": "string",
      "nullable": false,
      "description": "Machine-readable error code (see the error codes list); stable."
    },
    {
      "path": "message",
      "type": "string",
      "nullable": false,
      "description": "What went wrong and how to fix it, for humans and agents; wording may change."
    },
    {
      "path": "request_id",
      "type": "string",
      "nullable": false,
      "description": "Unique request ID; include it when contacting support."
    },
    {
      "path": "status",
      "type": "integer",
      "nullable": false,
      "description": "The HTTP status of the response, repeated for clients that only see the body."
    },
    {
      "path": "retryable",
      "type": "boolean",
      "nullable": false,
      "description": "True when repeating the same request later may succeed (after details.retry_after when present)."
    },
    {
      "path": "param",
      "type": "string",
      "nullable": true,
      "description": "The query parameter or header the error is about; null when it is not about one input."
    },
    {
      "path": "remediation",
      "type": "string",
      "nullable": false,
      "description": "What to do about this code, one sentence."
    },
    {
      "path": "doc_url",
      "type": "string",
      "nullable": false,
      "description": "URL of this code's documentation."
    },
    {
      "path": "details",
      "type": "object",
      "nullable": false,
      "description": "The code's dynamic values (the error codes list documents each code's keys); {} when none."
    },
    {
      "path": "charged",
      "type": "boolean",
      "nullable": false,
      "description": "Whether the request was charged: always false, failed requests are free."
    }
  ],
  "error_example": {
    "error": {
      "code": "invalid_input",
      "message": "unknown parameter \"link\"; did you mean \"url\"? This endpoint accepts: url, lang, max_age, fields.",
      "request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
      "status": 400,
      "retryable": false,
      "param": "link",
      "remediation": "Fix the parameter named in param (the message says how); for a link of the wrong kind, call the endpoint the message names.",
      "doc_url": "https://staging.socialtap.dev/docs/errors/#invalid_input",
      "details": {
        "accepted_params": [
          "url",
          "lang",
          "max_age",
          "fields"
        ],
        "suggestion": "url"
      },
      "charged": false
    }
  },
  "param_vocabulary": [
    {
      "name": "lang",
      "type": "string",
      "doc": "Preferred language: a BCP-47 tag such as en or pt-BR (case-insensitive; _ is read as -).",
      "aliases": [
        "hl",
        "lang_code",
        "language",
        "language_code",
        "locale"
      ],
      "lists": false,
      "format": "bcp47",
      "example": "en"
    },
    {
      "name": "country",
      "type": "string",
      "doc": "Region: an ISO 3166-1 alpha-2 country code such as US or BR (case-insensitive).",
      "aliases": [
        "country_code",
        "geo",
        "gl",
        "market",
        "region"
      ],
      "lists": false,
      "format": "iso3166-1-alpha-2",
      "example": "US"
    },
    {
      "name": "sort",
      "type": "string",
      "doc": "Order of the results.",
      "aliases": [
        "order",
        "order_by",
        "orderby",
        "sort_by",
        "sortby"
      ],
      "lists": false,
      "values": [
        "relevance",
        "newest",
        "oldest",
        "popular"
      ]
    },
    {
      "name": "since",
      "type": "string",
      "doc": "Only items at or after this time: RFC 3339 (2026-10-01T12:00:00Z) or a date (2026-10-01, midnight UTC). On newest-first lists, paging stops when it is reached, so later pages are not fetched or charged.",
      "aliases": [
        "after",
        "date_from",
        "from",
        "from_date",
        "min_date",
        "start",
        "start_date",
        "start_time"
      ],
      "lists": true,
      "format": "date-time",
      "example": "2026-10-01"
    },
    {
      "name": "until",
      "type": "string",
      "doc": "Only items before this time (exclusive): RFC 3339 or a date (midnight UTC).",
      "aliases": [
        "before",
        "date_to",
        "end",
        "end_date",
        "end_time",
        "max_date",
        "to",
        "to_date"
      ],
      "lists": true,
      "format": "date-time",
      "example": "2026-10-08"
    }
  ],
  "url_kinds": [
    {
      "id": "profile",
      "name": "profile"
    },
    {
      "id": "video",
      "name": "video"
    },
    {
      "id": "post",
      "name": "post"
    },
    {
      "id": "hashtag",
      "name": "hashtag"
    },
    {
      "id": "playlist",
      "name": "playlist"
    }
  ],
  "warnings": [
    {
      "code": "deprecated_endpoint",
      "description": "The endpoint is deprecated: it stops answering on its sunset date, after which it returns endpoint_retired. The message names the date and the replacement."
    }
  ],
  "changelog": [
    {
      "id": "2026-10-26-accounts",
      "date": "2026-10-26",
      "title": "Self-serve signup, test keys, API key management and usage",
      "kind": "added",
      "endpoints": [],
      "body": "You can now get started without talking to anyone, test for free, and manage your account through the API.\n\n- **Sign up** with your email address: the emailed link creates your account with free credits, a live key and a\n  test key, and opens the dashboard (keys, usage, balance, credit packs). **AI agents** can sign up with\n  `POST /v1/agent_signup`: they get a test key at once and a live key that works once you confirm by email.\n- **Test keys** (`st_test_…`) answer every endpoint from recorded responses, free and deterministic, and reserved\n  \"magic\" inputs trigger each error code, so integrations and CI can exercise every branch at no cost.\n- **API keys** have scopes (`endpoints`, `jobs`, `account:read`, `admin`), optional spend limits per day or month\n  (`spend_limit_reached`), expiry and IP allowlists, managed through `/v1/api_keys`.\n- **Account and usage**: `GET /v1/account` (balance, plan, the calling key), `GET /v1/usage` (requests and credits\n  over time by endpoint, key and outcome) and `GET /v1/credit_transactions` (the ledger). Set\n  `low_balance_threshold` to get a `credits.low` email and event.\n- `meta.mode` says whether a response came from a live or a test key. `forbidden` is now returned (with\n  `details.reason`), and a missing key's `unauthorized` says where to get one.",
      "changes": [
        "endpoint facebook.transcript: accepts video links",
        "endpoint instagram.transcript: accepts video links",
        "endpoint instagram.transcript: accepts post links",
        "endpoint tiktok.transcript: accepts video links",
        "endpoint tiktok.transcript: accepts post links",
        "endpoint youtube.transcript: accepts video links",
        "error code endpoint_retired added (HTTP 410)",
        "error code forbidden added (HTTP 403)",
        "error code idempotency_key_in_progress added (HTTP 409)",
        "error code idempotency_key_reused added (HTTP 422)",
        "error code insufficient_credits: details key credits_required added",
        "error code insufficient_credits: details key credits_available added",
        "error code invalid_cursor: details key reason added",
        "error code invalid_input: details key suggestion added",
        "error code invalid_input: details key accepted_params added",
        "error code invalid_input: details key url_kind added",
        "error code invalid_input: details key url_platform added",
        "error code invalid_input: details key accepted_url_kinds added",
        "error code invalid_input: details key endpoints added",
        "error code invalid_input: details key fields added",
        "error code job_not_found added (HTTP 404)",
        "error code overloaded: details key retry_after added",
        "error code rate_limited: details key retry_after added",
        "error code spend_limit_reached added (HTTP 402)",
        "header cache-control added",
        "header deprecation added",
        "header link added",
        "header ratelimit added",
        "header ratelimit-policy added",
        "header sunset added",
        "header traceparent added",
        "header x-client-request-id added",
        "common param fields added",
        "warning code deprecated_endpoint added",
        "shared param country added",
        "shared param lang added",
        "shared param since added",
        "shared param sort added",
        "shared param until added",
        "meta fields are part of the locked contract",
        "page fields are part of the locked contract",
        "error fields are part of the locked contract",
        "MCP tool find_endpoint added",
        "MCP tool get_transcript added",
        "header authorization added",
        "error code forbidden: details key reason added",
        "error code forbidden: details key required_scope added",
        "error code spend_limit_reached: details key api_key_id added",
        "error code spend_limit_reached: details key spend_limit added",
        "error code spend_limit_reached: details key interval added",
        "error code spend_limit_reached: details key used added",
        "error code spend_limit_reached: details key credits_required added",
        "error code spend_limit_reached: details key resets_at added",
        "error code unauthorized: details key signup_url added",
        "error code unauthorized: details key agent_signup_url added",
        "meta: field mode added",
        "MCP tool get_account added",
        "error code insufficient_credits: details key max_credits added",
        "header idempotency-key added",
        "header idempotent-replayed added",
        "header location added",
        "header prefer added",
        "header preference-applied added",
        "header webhook-id added",
        "header webhook-signature added",
        "header webhook-timestamp added",
        "event type job.completed added",
        "event type job.canceled added",
        "event type job.expired added",
        "event type credits.low added",
        "event type credits.depleted added",
        "event type api_key.spend_limit_reached added",
        "event type webhook.test added",
        "operation DELETE /v1/webhook_endpoints/{id} added (webhook_endpoints.delete)",
        "operation GET /v1/events added (events.list)",
        "operation GET /v1/events/{id} added (events.get)",
        "operation GET /v1/jobs added (jobs.list)",
        "operation GET /v1/jobs/{id} added (jobs.get)",
        "operation GET /v1/jobs/{id}/results added (jobs.results.list)",
        "operation GET /v1/jobs/{id}/results.jsonl added (jobs.results.download)",
        "operation GET /v1/webhook_endpoints added (webhook_endpoints.list)",
        "operation GET /v1/webhook_endpoints/{id} added (webhook_endpoints.get)",
        "operation GET /v1/webhook_endpoints/{id}/deliveries added (webhook_endpoints.deliveries.list)",
        "operation PATCH /v1/webhook_endpoints/{id} added (webhook_endpoints.update)",
        "operation POST /v1/events/{id}/redeliver added (events.redeliver)",
        "operation POST /v1/jobs added (jobs.create)",
        "operation POST /v1/jobs/{id}/cancel added (jobs.cancel)",
        "operation POST /v1/webhook_endpoints added (webhook_endpoints.create)",
        "operation POST /v1/webhook_endpoints/{id}/rotate_secret added (webhook_endpoints.rotate_secret)",
        "operation POST /v1/webhook_endpoints/{id}/test added (webhook_endpoints.test)",
        "MCP tool create_job added",
        "MCP tool get_job added",
        "endpoint tiktok.profile added (/v1/tiktok/profile)",
        "resource profile is live",
        "MCP tool get_profile added",
        "endpoint instagram.post added (/v1/instagram/post)",
        "resource post is live",
        "MCP tool get_post added",
        "endpoint facebook.search_ads added (/v1/facebook/search_ads)",
        "resource ad is live",
        "MCP tool search added",
        "endpoint youtube.comments added (/v1/youtube/comments)",
        "resource comment is live",
        "MCP tool list_comments added"
      ],
      "doc_url": "https://staging.socialtap.dev/docs/changelog/#2026-10-26-accounts"
    },
    {
      "id": "2026-10-26-api-conventions",
      "date": "2026-10-26",
      "title": "Request conventions, richer errors and response metadata",
      "kind": "changed",
      "endpoints": [],
      "body": "Every endpoint now follows the same request conventions, and every response tells you more about itself.\n\n- **Unknown parameters are rejected** with `invalid_input`, free, naming the parameter and the one you most likely\n  meant (`username` → `handle`). Previously they were ignored. Common parameters (`max_age`, `fields`) are always\n  known.\n- **Errors carry everything needed to react** without parsing the message: `status`, `retryable`, `param`,\n  `remediation`, `doc_url`, `details` (for example `credits_required`, or the endpoint a link belongs to) and\n  `charged` (always false). `retry-after` is never sent on credit errors.\n- **A link of the wrong kind** (a profile link sent to a video endpoint, a link from another platform) is answered with\n  the endpoint that takes it.\n- **`meta`** gains `input` (what your url resolved to), `credits_remaining`, `client_request_id` and `warnings`.\n- **`fields`** selects only the fields you need, after the cache, at no cost.\n- **Lists** gain `page.end_reason` and a `Link: rel=\"next\"` header; `since` stops paging on newest-first lists.\n- **Headers**: send `x-client-request-id` to have your own ID echoed and logged, and `traceparent` to match traces.\n  `RateLimit-Policy` and `RateLimit` describe the account's runaway-protection ceiling, and `Cache-Control: max-age`\n  works like `max_age`.\n- Deprecated endpoints will announce themselves with `Deprecation` and `Sunset` headers and a warning, and answer\n  `endpoint_retired` after their sunset. This changelog is also a [JSON Feed](/changelog.json).",
      "changes": [],
      "doc_url": "https://staging.socialtap.dev/docs/changelog/#2026-10-26-api-conventions"
    },
    {
      "id": "2026-10-26-jobs",
      "date": "2026-10-26",
      "title": "Background jobs and batches, webhooks, events and Idempotency-Key",
      "kind": "added",
      "endpoints": [],
      "body": "Run many requests, or one long one, without holding a connection open, and hear back when they are done.\n\n- **Jobs**: `POST /v1/jobs` runs a batch of endpoint requests in the background, at each endpoint's own price, and\n  `max_pages` walks a whole list server-side. With `?wait` a short job answers inline (a synchronous batch); otherwise\n  poll `GET /v1/jobs/{id}` (free), page through `GET /v1/jobs/{id}/results` as items finish, or download every result\n  from the job's signed `results_url`. `Prefer: respond-async` turns any endpoint request into a one-request job.\n- **Credits** are set aside per job (`max_credits`) and charged per item as it ends; failed items are free. A key's\n  spend limit holds through its jobs, and a test key's jobs answer from recorded responses, free.\n- **Webhooks** (Standard Webhooks signatures, retried for days, secrets rotated without downtime) deliver\n  `job.completed`, `job.canceled`, `job.expired`, `credits.low`, `credits.depleted` and\n  `api_key.spend_limit_reached`; `/v1/events` lists every event and redelivers one on request.\n- **Idempotency-Key** on every POST: retrying with the same key and body never repeats the work.\n- The SDKs gain `jobs`, `webhookEndpoints` and `events`, a create-and-wait helper and webhook signature verification.",
      "changes": [],
      "doc_url": "https://staging.socialtap.dev/docs/changelog/#2026-10-26-jobs"
    }
  ],
  "source_kinds": [
    {
      "id": "captions",
      "name": "Captions",
      "description": "The platform's own caption track (uploaded or auto-generated)."
    },
    {
      "id": "asr",
      "name": "Speech-to-text",
      "description": "Transcribed by SocialTap from the media's audio when the platform has no captions."
    },
    {
      "id": "api",
      "name": "Platform API",
      "description": "The platform's public, logged-out API."
    },
    {
      "id": "page",
      "name": "Public page",
      "description": "Data embedded in the platform's public web page."
    }
  ],
  "docs": {
    "account": "/docs/account/",
    "agents": "/docs/agents/",
    "changelog": "/docs/changelog/",
    "changelog_feed": "/changelog.json",
    "dashboard": "/dashboard/",
    "endpoint": "/apis/{platform}/{name}/",
    "error": "/docs/errors/#{code}",
    "errors": "/docs/errors/",
    "llms_full_txt": "/llms-full.txt",
    "llms_txt": "/llms.txt",
    "reference": "/docs/reference/",
    "signup": "/signup/",
    "signup_confirm": "/signup/confirm/",
    "skills": "/.well-known/agent-skills/index.json",
    "test_mode": "/docs/test-mode/"
  },
  "mcp": {
    "path": "/mcp",
    "transport": "streamable-http",
    "name": "dev.socialtap/mcp",
    "title": "SocialTap",
    "version": "1.84.0",
    "instructions": "SocialTap returns public social media data (transcripts today; profiles, posts and comments next) from every major platform through one API key, one credit balance and one response schema per resource. Use these tools whenever a task needs public data from Facebook, Instagram, TikTok or YouTube.\n\nTools:\n- get_post: Get a post on Instagram\n- get_profile: Get a profile on TikTok\n- get_transcript: Get a transcript on Facebook, Instagram, TikTok or YouTube\n- list_comments: List comments on YouTube\n- search: Search ads on Facebook\n- find_endpoint: Find an endpoint\n- get_account: Get account balance\n- create_job: Create a background job\n- get_job: Get a background job\n\nRules:\n- Pass platform plus the link, handle or query the tool asks for; find_endpoint finds the right tool for a need or a link.\n- Prices are credits per successful call (per page for lists); each tool states its price. Every result states credits_charged and credits_remaining. Failed calls are free. Pass max_age=<seconds> to accept a stored copy: cache hits are free.\n- Results are concise by default (detail=\"full\" returns every field) and cut at about 20,000 tokens. When a result has next_cursor, call the same tool with the same arguments and cursor=next_cursor for the rest; lists return one page per call.\n- Errors are results with isError and an error object: retry only when error.retryable is true (after details.retry_after seconds when set); otherwise follow error.remediation. On insufficient_credits stop and tell the user.\n- Keys starting with st_test_ are test keys: every tool answers from recorded responses, free and deterministic.\n- null means unknown, never zero.\n\nDocs for agents: https://staging.socialtap.dev/llms.txt (index), https://staging.socialtap.dev/docs/agents/ (this server and the API).",
    "auth_headers": [
      "authorization",
      "x-api-key"
    ],
    "max_result_tokens": 20000,
    "tools": [
      {
        "name": "get_post",
        "title": "Get a post",
        "description": "Get a public post on Instagram. A public post (video, reel, short, photo, carousel, tweet or text) with its author, media and engagement metrics. Pass platform and url (its link or ID). Price per successful call: 1 credit. Failed calls and cache hits (max_age accepts a stored copy) are free; every result states credits_charged and credits_remaining. Results over about 20,000 tokens are cut: when next_cursor is set, call again with the same arguments and cursor=next_cursor. For other data or platforms, call find_endpoint. Notes: Instagram: Media URLs are Instagram CDN links that expire: media[].expires_at says when; download what you need before then. Instagram: view_count is set for single videos and reels only (Instagram shows no view count for photos and carousels). like_count and view_count are null when the owner hides them. Instagram: Instagram does not expose share or save counts, pinned state, sponsorship labels or the caption's language to logged-out visitors: those fields are null. Instagram: Private, age-restricted and deleted posts return private_or_unavailable or not_found and are not charged.",
        "input_schema": {
          "type": "object",
          "properties": {
            "platform": {
              "description": "The platform the link is on.",
              "enum": [
                "instagram"
              ],
              "type": "string"
            },
            "url": {
              "description": "Instagram post, reel or carousel URL (instagram.com/p/…, /reel/…, /reels/…, /tv/…, /<username>/p/…, m.instagram.com, instagr.am, instagram.com/share/… links) or a raw shortcode. Tracking params and img_index are ignored: the whole post is returned.",
              "maxLength": 2048,
              "type": "string"
            },
            "detail": {
              "default": "concise",
              "description": "concise (default) returns platform, id, url, type, title, text, published_at, author.username, author.display_name, duration_ms, hashtags, metrics; full returns every field (adds platform_type, language, coauthors, thumbnail_url, media, mentions, is_pinned, is_sponsored, is_nsfw, approximate_fields). Same price.",
              "enum": [
                "concise",
                "full"
              ],
              "type": "string"
            },
            "max_age": {
              "description": "Accept a cached response up to this many seconds old. Cache hits cost 0 credits. Omit to always fetch fresh. The Cache-Control: max-age=N request header is equivalent; max_age wins when both are sent.",
              "minimum": 0,
              "type": "integer"
            },
            "cursor": {
              "description": "next_cursor from this tool's previous result, to continue a cut result. Send the same other arguments.",
              "type": "string"
            }
          },
          "required": [
            "platform",
            "url"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Get a post",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": true
        }
      },
      {
        "name": "get_profile",
        "title": "Get a profile",
        "description": "Get a public profile on TikTok. A public account (creator, channel, page, company or community) with its bio, links and follower metrics. Pass platform and handle (a profile link, @handle, username or ID). Price per successful call: 1 credit. Failed calls and cache hits (max_age accepts a stored copy) are free; every result states credits_charged and credits_remaining. Results over about 20,000 tokens are cut: when next_cursor is set, call again with the same arguments and cursor=next_cursor. For other data or platforms, call find_endpoint. Notes: TikTok: Private accounts return their profile with is_private true; their videos are not public. TikTok: type is organization for accounts TikTok marks as organizations and unknown otherwise: TikTok does not label people. TikTok: category is set only for business accounts. location, country_code, banner_url and metrics.total_view_count are always null: public TikTok profiles do not show them. TikTok: When the profile page is unavailable the answer comes from TikTok's embed profile: follower and like counts are then rounded (listed in approximate_fields), and the join date, bio link, category, type and video count are unknown. TikTok: Share links (vm.tiktok.com) are not accepted: pass the username or the profile URL.",
        "input_schema": {
          "type": "object",
          "properties": {
            "platform": {
              "description": "The platform the profile is on.",
              "enum": [
                "tiktok"
              ],
              "type": "string"
            },
            "handle": {
              "description": "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.",
              "maxLength": 2048,
              "type": "string"
            },
            "detail": {
              "default": "concise",
              "description": "concise (default) returns platform, id, username, display_name, url, type, bio, is_verified, is_private, category, external_links, location, created_at, metrics; full returns every field (adds avatar_url, banner_url, country_code, approximate_fields). Same price.",
              "enum": [
                "concise",
                "full"
              ],
              "type": "string"
            },
            "max_age": {
              "description": "Accept a cached response up to this many seconds old. Cache hits cost 0 credits. Omit to always fetch fresh. The Cache-Control: max-age=N request header is equivalent; max_age wins when both are sent.",
              "minimum": 0,
              "type": "integer"
            },
            "cursor": {
              "description": "next_cursor from this tool's previous result, to continue a cut result. Send the same other arguments.",
              "type": "string"
            }
          },
          "required": [
            "platform",
            "handle"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Get a profile",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": true
        }
      },
      {
        "name": "get_transcript",
        "title": "Get a transcript",
        "description": "Get the transcript of a public video or post on Facebook, Instagram, TikTok or YouTube. Timed transcript of a public video: the platform's caption track, or speech-to-text on its audio when there is none. Pass platform and url (its link or ID). Price per successful call: 1 credit (captions); or 2 credits + 2 per started minute of media, at most 122 (speech-to-text). Failed calls and cache hits (max_age accepts a stored copy) are free; every result states credits_charged and credits_remaining. Results over about 20,000 tokens are cut: when next_cursor is set, call again with the same arguments and cursor=next_cursor. For other data or platforms, call find_endpoint. Notes: Facebook: Videos that Facebook only serves to logged-in users return private_or_unavailable. Instagram: Most results come from speech-to-text (meta.source = asr) and are priced per minute. Instagram: Image posts return invalid_input; videos without speech return no_transcript. TikTok: Photo posts have no audio track and return no_transcript. TikTok: Videos with licensed music and no speech may transcribe the song lyrics. YouTube: Manual captions are preferred over auto-generated ones; is_auto_generated tells you which you got. YouTube: Private, members-only, rental and age-restricted videos return private_or_unavailable.",
        "input_schema": {
          "type": "object",
          "properties": {
            "platform": {
              "description": "The platform the link is on.",
              "enum": [
                "facebook",
                "instagram",
                "tiktok",
                "youtube"
              ],
              "type": "string"
            },
            "url": {
              "description": "Facebook: Facebook video URL (reel, watch, page video, video.php, m./mbasic./web. hosts, fb.watch and share/v|r links) or a numeric video ID. Instagram: Instagram reel/post URL (instagram.com/reel/…, /reels/…, /p/…, /tv/…, instagr.am, instagram.com/share/… links) or a raw shortcode. For carousel posts, ?img_index=N selects the item; default is the first video. TikTok: TikTok video URL (tiktok.com/@user/video/<id>, m.tiktok.com/v/<id>.html, vm.tiktok.com/<code>, vt.tiktok.com/<code>, tiktok.com/t/<code>, embed URLs) or a numeric video ID. YouTube: YouTube video URL (watch, youtu.be, shorts, live, embed, m./music. hosts) or an 11-character video ID.",
              "maxLength": 2048,
              "type": "string"
            },
            "lang": {
              "description": "Only on TikTok or YouTube. TikTok: Preferred language: a BCP-47 tag such as en or pt-BR (case-insensitive; _ is read as -). Selects the caption track (TikTok also offers its own translations); falls back to the video's original language, then any track. TikTok's three-letter codes (eng-US) work too. YouTube: Preferred language: a BCP-47 tag such as en or pt-BR (case-insensitive; _ is read as -). Selects the caption track; a manual track is preferred over an auto-generated one. Default: the video's original-language manual track, else its auto-generated track.",
              "type": "string"
            },
            "detail": {
              "default": "concise",
              "description": "concise (default) returns platform, id, url, language, source, is_auto_generated, available_languages, duration_ms, text; full returns every field (adds segments). Same price.",
              "enum": [
                "concise",
                "full"
              ],
              "type": "string"
            },
            "max_age": {
              "description": "Accept a cached response up to this many seconds old. Cache hits cost 0 credits. Omit to always fetch fresh. The Cache-Control: max-age=N request header is equivalent; max_age wins when both are sent.",
              "minimum": 0,
              "type": "integer"
            },
            "cursor": {
              "description": "next_cursor from this tool's previous result, to continue a cut result. Send the same other arguments.",
              "type": "string"
            }
          },
          "required": [
            "platform",
            "url"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Get a transcript",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": true
        }
      },
      {
        "name": "list_comments",
        "title": "List comments",
        "description": "List the comments of a public post on YouTube, one page per call. A public comment or reply on a post, with its author, thread position and like/reply counts. Pass platform and url (its link or ID). Price per successful page: 1 credit. Failed calls and cache hits (max_age accepts a stored copy) are free; every result states credits_charged and credits_remaining. Results over about 20,000 tokens are cut: when next_cursor is set, call again with the same arguments and cursor=next_cursor. Follow next_cursor while it is set to get further pages; an empty or short page is not the end. For other data or platforms, call find_endpoint. Notes: YouTube: YouTube shows comment times as relative labels (\"3 weeks ago\"): published_at is the latest time the label allows and is listed in approximate_fields, so since and until are approximate to the label's unit. YouTube: Like and reply counts above a thousand are rounded by YouTube (\"4.8K\") and listed in approximate_fields. YouTube: Comments show the author's @handle; author.display_name is null. YouTube: The newest order includes comments YouTube's Top ranking holds back as potential spam. YouTube: Videos with comments turned off and private videos return private_or_unavailable; deleted videos return not_found. YouTube: Replies are not included; metrics.reply_count says how many a comment has.",
        "input_schema": {
          "type": "object",
          "properties": {
            "platform": {
              "description": "The platform the link is on.",
              "enum": [
                "youtube"
              ],
              "type": "string"
            },
            "url": {
              "description": "YouTube video URL (watch, youtu.be, shorts, live, embed, m./music. hosts) or an 11-character video ID.",
              "maxLength": 2048,
              "type": "string"
            },
            "sort": {
              "description": "Order of the results. newest (default): newest first. popular: YouTube's Top comments ranking. Either way the video's pinned comment comes first.",
              "enum": [
                "newest",
                "popular"
              ],
              "type": "string"
            },
            "since": {
              "description": "Only items at or after this time: RFC 3339 (2026-10-01T12:00:00Z) or a date (2026-10-01, midnight UTC). On newest-first lists, paging stops when it is reached, so later pages are not fetched or charged. Compared with published_at, which YouTube only shows as a relative label (\"2 days ago\"): the walk stops at the first comment whose label is older, so comments up to one label unit older can be included.",
              "type": "string"
            },
            "until": {
              "description": "Only items before this time (exclusive): RFC 3339 or a date (midnight UTC).",
              "type": "string"
            },
            "limit": {
              "description": "Items per page, 1 to the endpoint's page size (the default and maximum; larger values are reduced). Reduces payload, not price: each page costs the same.",
              "minimum": 1,
              "type": "integer"
            },
            "detail": {
              "default": "concise",
              "description": "concise (default) returns id, parent_id, text, author.username, published_at, is_pinned, metrics; full returns every field (adds platform, post_id, url, edited_at, is_edited, is_liked_by_author, is_by_post_author, approximate_fields). Same price.",
              "enum": [
                "concise",
                "full"
              ],
              "type": "string"
            },
            "max_age": {
              "description": "Accept a cached response up to this many seconds old. Cache hits cost 0 credits. Omit to always fetch fresh. The Cache-Control: max-age=N request header is equivalent; max_age wins when both are sent.",
              "minimum": 0,
              "type": "integer"
            },
            "cursor": {
              "description": "next_cursor from this tool's previous result, to continue a cut result or get the next page. Send the same other arguments.",
              "type": "string"
            }
          },
          "required": [
            "platform",
            "url"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "List comments",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": true
        }
      },
      {
        "name": "search",
        "title": "Search ads",
        "description": "Search public ads on Facebook by query. Pass platform and query (the search terms). Price per successful page: 1 credit. Failed calls and cache hits (max_age accepts a stored copy) are free; every result states credits_charged and credits_remaining. Results over about 20,000 tokens are cut: when next_cursor is set, call again with the same arguments and cursor=next_cursor. Follow next_cursor while it is set to get further pages; an empty or short page is not the end. For other data or platforms, call find_endpoint. Notes: Facebook: Payer, impressions, reach and spend are disclosed only for political and social-issue ads; they are null for other ads. Facebook: Dates are whole days (Meta reports the day an ad first and last ran, US Pacific time), so they are listed in approximate_fields. Facebook: since and until filter by the day an ad first ran (first_shown_at); a filtered page can hold fewer ads than the page size, and that is not the end of the list. Facebook: Ads that share one creative are grouped by Meta; each ad of a group is returned as its own item. Facebook: Languages, targeting and EU reach by country are not part of the search results; those fields are empty or null.",
        "input_schema": {
          "type": "object",
          "properties": {
            "platform": {
              "description": "The platform to search.",
              "enum": [
                "facebook"
              ],
              "type": "string"
            },
            "type": {
              "description": "What to search for.",
              "enum": [
                "ads"
              ],
              "type": "string"
            },
            "query": {
              "description": "Keywords matched against the ad's text, in any order. Wrap the query in double quotes (\"cold brew\") to match an exact phrase.",
              "maxLength": 200,
              "type": "string"
            },
            "country": {
              "description": "Region: an ISO 3166-1 alpha-2 country code such as US or BR (case-insensitive). Only ads delivered in this country. All countries when omitted.",
              "type": "string"
            },
            "status": {
              "description": "Only ads that are still running (active), only ads that stopped (inactive), or both (all, the default).",
              "enum": [
                "all",
                "active",
                "inactive"
              ],
              "type": "string"
            },
            "since": {
              "description": "Only items at or after this time: RFC 3339 (2026-10-01T12:00:00Z) or a date (2026-10-01, midnight UTC). On newest-first lists, paging stops when it is reached, so later pages are not fetched or charged. Filters by first_shown_at, the day the ad first ran.",
              "type": "string"
            },
            "until": {
              "description": "Only items before this time (exclusive): RFC 3339 or a date (midnight UTC). Filters by first_shown_at, the day the ad first ran.",
              "type": "string"
            },
            "limit": {
              "description": "Items per page, 1 to the endpoint's page size (the default and maximum; larger values are reduced). Reduces payload, not price: each page costs the same.",
              "minimum": 1,
              "type": "integer"
            },
            "detail": {
              "default": "concise",
              "description": "concise (default) returns platform, id, url, advertiser.username, advertiser.display_name, is_active, first_shown_at, last_shown_at, type, creatives[].title, creatives[].body, creatives[].link_url, impressions, spend; full returns every field (adds paid_by, publisher_platforms, languages, country_codes, is_political, reach, audience_size, approximate_fields). Same price.",
              "enum": [
                "concise",
                "full"
              ],
              "type": "string"
            },
            "max_age": {
              "description": "Accept a cached response up to this many seconds old. Cache hits cost 0 credits. Omit to always fetch fresh. The Cache-Control: max-age=N request header is equivalent; max_age wins when both are sent.",
              "minimum": 0,
              "type": "integer"
            },
            "cursor": {
              "description": "next_cursor from this tool's previous result, to continue a cut result or get the next page. Send the same other arguments.",
              "type": "string"
            }
          },
          "required": [
            "platform",
            "type",
            "query"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Search ads",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": true
        }
      },
      {
        "name": "find_endpoint",
        "title": "Find an endpoint",
        "description": "Find which SocialTap tool and endpoint serves a need (\"TikTok comments\", \"YouTube transcript\") or a link: returns the best matches with the tool and arguments to call, the price, the parameters and an example result. Use it when no tool obviously fits, or to check which platforms and links a tool takes. Free, and it works without an API key.",
        "input_schema": {
          "type": "object",
          "properties": {
            "query": {
              "description": "What you need, in plain words, or a link to a post, video or profile.",
              "maxLength": 2048,
              "minLength": 1,
              "type": "string"
            },
            "platform": {
              "description": "Only endpoints on this platform.",
              "enum": [
                "facebook",
                "instagram",
                "tiktok",
                "youtube"
              ],
              "type": "string"
            },
            "limit": {
              "default": 3,
              "description": "How many matches to return.",
              "maximum": 10,
              "minimum": 1,
              "type": "integer"
            }
          },
          "required": [
            "query"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Find an endpoint",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        }
      },
      {
        "name": "get_account",
        "title": "Get account balance",
        "description": "Get the account, balance and calling key. The account's balance and plan, the low-balance alert threshold, and the calling key: its mode, scopes, spend limit (with what it used this interval) and expiry. Free; never cached. A pending agent key may call it (and nothing else) to learn when its owner confirmed.\n\n**Auth:** an API key with the `account:read` scope, or the dashboard session.\n\n**Test mode:** A test key sees the fixed test balance. Use it to check the credit balance before a large task; every other result already states credits_remaining.",
        "input_schema": {
          "type": "object",
          "properties": {},
          "required": [],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Get account balance",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        }
      },
      {
        "name": "create_job",
        "title": "Create a background job",
        "description": "Run many requests (or one long one) as a job. Runs up to `max_requests` (1000) endpoint requests in the background: each item is exactly the request its endpoint answers synchronously, at the same price (failed items are free), with the same caches. `max_pages` walks a list endpoint server-side with its cursor. The job reserves `max_credits` (default: the sum of every item's maximum price) at creation, against the balance and the calling key's spend limit, charges each item as it ends and releases the rest when the job ends. With `wait`, the answer is 200 with the first page of results when the job ends in time (a synchronous batch), else 202 with the job; poll `GET /v1/jobs/{id}` (free) or subscribe to `job.completed`.\n\n**Auth:** an API key with the `jobs` scope, or the dashboard session.\n\n**Test mode:** A test key's job runs every item in test mode: each gets its endpoint's test-mode answer (recorded responses, magic inputs), free; nothing is held or charged. A test key sees test jobs only.\n\nSend an `idempotency-key` header to retry safely: the same key and body within 1 day never repeat the work. Use it instead of a loop of single calls for many items or for walking a whole list; poll it with get_job.",
        "input_schema": {
          "type": "object",
          "properties": {
            "wait": {
              "description": "Seconds to wait for the job to end before answering. If it ends in time the answer is 200 with the first page of results (a synchronous batch); otherwise 202 with the job as it is.",
              "maximum": 60,
              "minimum": 0,
              "type": "integer"
            },
            "max_age": {
              "description": "Accept cached results up to this many seconds old for every item (free cache hits), like the endpoints' max_age.",
              "minimum": 0,
              "type": "integer"
            },
            "max_credits": {
              "description": "Most the job may spend; reserved at creation. Default: the sum of every item's maximum price (times its max_pages). Items that would go over it are canceled.",
              "minimum": 1,
              "type": "integer"
            },
            "metadata": {
              "additionalProperties": {
                "maxLength": 500,
                "type": "string"
              },
              "description": "Up to metadata_max_keys string pairs of your own, returned with the job and in its events.",
              "type": "object"
            },
            "requests": {
              "description": "The requests to run, 1 to max_requests; custom_id must be unique within the job.",
              "items": {
                "properties": {
                  "custom_id": {
                    "description": "Your ID for the item (letters, digits, _ and -), echoed on its results.",
                    "maxLength": 64,
                    "minLength": 1,
                    "type": "string"
                  },
                  "endpoint": {
                    "description": "The endpoint ID (youtube.transcript).",
                    "type": "string"
                  },
                  "max_pages": {
                    "description": "List endpoints only: walk up to this many pages with the cursor (default 1). Each page is a result and costs a page's price.",
                    "maximum": 1000,
                    "minimum": 1,
                    "type": "integer"
                  },
                  "params": {
                    "additionalProperties": {},
                    "description": "The endpoint's query params, as you would send them to it (strings, numbers or booleans).",
                    "type": "object"
                  }
                },
                "required": [
                  "custom_id",
                  "endpoint",
                  "params"
                ],
                "type": "object"
              },
              "type": "array"
            },
            "webhook_url": {
              "description": "Also deliver this job's job.* events to this URL: the URL of one of the account's webhook endpoints (its secret signs them).",
              "maxLength": 2048,
              "type": "string"
            }
          },
          "required": [
            "requests"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Create a background job",
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": true
        }
      },
      {
        "name": "get_job",
        "title": "Get a background job",
        "description": "Get a job (free to poll). The job's status, per-state request counts and credits. Free: poll it, or long-poll with `wait`.\n\n**Auth:** an API key with the `jobs` scope, or the dashboard session.\n\n**Test mode:** A test key's job runs every item in test mode: each gets its endpoint's test-mode answer (recorded responses, magic inputs), free; nothing is held or charged. A test key sees test jobs only. Poll a job created with create_job until it completes; polling is free.",
        "input_schema": {
          "type": "object",
          "properties": {
            "id": {
              "description": "Job ID (job_…).",
              "type": "string"
            },
            "wait": {
              "description": "Seconds to wait for the job to end before answering (a long poll): the answer is the job as it is then.",
              "maximum": 60,
              "minimum": 0,
              "type": "integer"
            }
          },
          "required": [
            "id"
          ],
          "additionalProperties": false
        },
        "annotations": {
          "title": "Get a background job",
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        }
      }
    ]
  },
  "test_mode": {
    "key_prefix": "st_test_",
    "balance": 10000,
    "magic": {
      "handle": "socialtap_test_{code}",
      "query": "socialtap_test_{code}",
      "url": "https://socialtap.dev/test/{code}"
    },
    "codes": [
      "invalid_input",
      "invalid_cursor",
      "insufficient_credits",
      "spend_limit_reached",
      "rate_limited",
      "not_found",
      "private_or_unavailable",
      "no_transcript",
      "upstream_blocked",
      "upstream_error",
      "overloaded",
      "internal"
    ],
    "cursor": "t1.",
    "endpoints": {
      "facebook.search_ads": {
        "identity": "query",
        "codes": [
          "invalid_input",
          "invalid_cursor",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "query": "groceries delivered",
              "status": "all"
            },
            "inputs": [
              "groceries delivered"
            ],
            "status": 200
          },
          {
            "params": {
              "country": "US",
              "query": "coffee",
              "status": "all"
            },
            "inputs": [
              "coffee"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "country": "US",
              "query": "coffee",
              "status": "all"
            },
            "inputs": [
              "coffee"
            ],
            "status": 200,
            "shadowed": true
          },
          {
            "params": {
              "country": "US",
              "query": "\"cold brew\"",
              "status": "active"
            },
            "inputs": [
              "\"cold brew\""
            ],
            "status": 200
          },
          {
            "params": {
              "country": "US",
              "query": "coffee",
              "since": "2026-09-01T00:00:00Z",
              "status": "all",
              "until": "2026-10-01T00:00:00Z"
            },
            "inputs": [
              "coffee"
            ],
            "status": 200
          },
          {
            "params": {
              "country": "GB",
              "query": "coffee",
              "status": "inactive"
            },
            "inputs": [
              "coffee"
            ],
            "status": 200
          },
          {
            "params": {
              "country": "US",
              "query": "zzqxjvbnmqwplkk",
              "status": "all"
            },
            "inputs": [
              "zzqxjvbnmqwplkk"
            ],
            "status": 200
          },
          {
            "params": {
              "country": "JP",
              "query": "コーヒー",
              "status": "all"
            },
            "inputs": [
              "コーヒー"
            ],
            "status": 200
          },
          {
            "params": {
              "country": "US",
              "query": "voting information center",
              "status": "all"
            },
            "inputs": [
              "voting information center"
            ],
            "status": 200
          },
          {
            "params": {
              "query": "running shoes",
              "since": "2026-10-01T00:00:00Z",
              "status": "all"
            },
            "inputs": [
              "running shoes"
            ],
            "status": 200
          },
          {
            "params": {
              "country": "XX",
              "query": "espresso",
              "status": "all"
            },
            "inputs": [
              "espresso"
            ],
            "status": 400,
            "error_code": "invalid_input"
          }
        ]
      },
      "facebook.transcript": {
        "identity": "url",
        "codes": [
          "invalid_input",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "no_transcript",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "url": "https://www.facebook.com/reel/1718871615853822/"
            },
            "inputs": [
              "https://www.facebook.com/reel/1718871615853822/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/3034943296845097/"
            },
            "inputs": [
              "https://www.facebook.com/reel/3034943296845097/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/738566979178125/"
            },
            "inputs": [
              "https://www.facebook.com/reel/738566979178125/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/1755978666533018/"
            },
            "inputs": [
              "https://www.facebook.com/reel/1755978666533018/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://fb.watch/G1pb1cwZsM/"
            },
            "inputs": [
              "https://fb.watch/G1pb1cwZsM/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/1570030614893129/"
            },
            "inputs": [
              "https://www.facebook.com/reel/1570030614893129/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/1113509447153884/"
            },
            "inputs": [
              "https://www.facebook.com/reel/1113509447153884/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://fb.watch/G-oJWeu2nt/"
            },
            "inputs": [
              "https://fb.watch/G-oJWeu2nt/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/1803909017420449/"
            },
            "inputs": [
              "https://www.facebook.com/reel/1803909017420449/"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/1408202287429345/"
            },
            "inputs": [
              "https://www.facebook.com/reel/1408202287429345/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.facebook.com/reel/3034943296845097/"
            },
            "inputs": [
              "https://www.facebook.com/reel/3034943296845097/"
            ],
            "status": 404,
            "error_code": "no_transcript",
            "shadowed": true
          }
        ]
      },
      "instagram.post": {
        "identity": "url",
        "codes": [
          "invalid_input",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "url": "https://www.instagram.com/p/De492EVDJd3/"
            },
            "inputs": [
              "https://www.instagram.com/p/De492EVDJd3/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/DexcKhDDno3/"
            },
            "inputs": [
              "https://www.instagram.com/p/DexcKhDDno3/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/DeuOR-CjKE9/"
            },
            "inputs": [
              "https://www.instagram.com/p/DeuOR-CjKE9/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De1S3KpCXf1/"
            },
            "inputs": [
              "https://www.instagram.com/p/De1S3KpCXf1/"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De1S3KpCXf0/"
            },
            "inputs": [
              "https://www.instagram.com/p/De1S3KpCXf0/"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/ChdZ_3UALgB/"
            },
            "inputs": [
              "https://www.instagram.com/p/ChdZ_3UALgB/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/share/reel/_oDaXMUlS/"
            },
            "inputs": [
              "https://www.instagram.com/share/reel/_oDaXMUlS/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/BBBBBBBBBBB/"
            },
            "inputs": [
              "https://www.instagram.com/p/BBBBBBBBBBB/"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/DDDDDDDDDDD/"
            },
            "inputs": [
              "https://www.instagram.com/p/DDDDDDDDDDD/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/CCCCCCCCCCC/"
            },
            "inputs": [
              "https://www.instagram.com/p/CCCCCCCCCCC/"
            ],
            "status": 503,
            "error_code": "upstream_blocked"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/EEEEEEEEEEE/"
            },
            "inputs": [
              "https://www.instagram.com/p/EEEEEEEEEEE/"
            ],
            "status": 200
          }
        ]
      },
      "instagram.transcript": {
        "identity": "url",
        "codes": [
          "invalid_input",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "no_transcript",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "url": "https://www.instagram.com/p/De11ONFn3Xk/"
            },
            "inputs": [
              "https://www.instagram.com/p/De11ONFn3Xk/"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De1S3KpCXf0/"
            },
            "inputs": [
              "https://www.instagram.com/p/De1S3KpCXf0/"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De4J7SciJp7/"
            },
            "inputs": [
              "https://www.instagram.com/p/De4J7SciJp7/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De492EVDJd3/"
            },
            "inputs": [
              "https://www.instagram.com/p/De492EVDJd3/"
            ],
            "status": 400,
            "error_code": "invalid_input"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/DexcKhDDno3/?img_index=2"
            },
            "inputs": [
              "https://www.instagram.com/p/DexcKhDDno3/?img_index=2"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De1S3KpCXf1/"
            },
            "inputs": [
              "https://www.instagram.com/p/De1S3KpCXf1/"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/DeuOR-CjKE9/"
            },
            "inputs": [
              "https://www.instagram.com/p/DeuOR-CjKE9/"
            ],
            "status": 400,
            "error_code": "invalid_input"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De1S3KpCXf0/"
            },
            "inputs": [
              "https://www.instagram.com/p/De1S3KpCXf0/"
            ],
            "status": 404,
            "error_code": "no_transcript",
            "shadowed": true
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/ChdZ_3UALgB/"
            },
            "inputs": [
              "https://www.instagram.com/p/ChdZ_3UALgB/"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.instagram.com/share/reel/_oDaXMUlS/"
            },
            "inputs": [
              "https://www.instagram.com/share/reel/_oDaXMUlS/"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/De6q2K4lYUd/"
            },
            "inputs": [
              "https://www.instagram.com/p/De6q2K4lYUd/"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/"
            ],
            "status": 403,
            "error_code": "private_or_unavailable",
            "shadowed": true
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/"
            ],
            "status": 404,
            "error_code": "no_transcript",
            "shadowed": true
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/?img_index=1"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/?img_index=1"
            ],
            "status": 400,
            "error_code": "invalid_input"
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/"
            ],
            "status": 403,
            "error_code": "private_or_unavailable",
            "shadowed": true
          },
          {
            "params": {
              "url": "https://www.instagram.com/p/AAAAAAAAAAA/"
            },
            "inputs": [
              "https://www.instagram.com/p/AAAAAAAAAAA/"
            ],
            "status": 503,
            "error_code": "upstream_blocked",
            "shadowed": true
          }
        ]
      },
      "tiktok.profile": {
        "identity": "handle",
        "codes": [
          "invalid_input",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "handle": "qq7x2nouser0z"
            },
            "inputs": [
              "qq7x2nouser0z"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "handle": "scout2015"
            },
            "inputs": [
              "scout2015",
              "https://www.tiktok.com/@scout2015"
            ],
            "status": 200
          },
          {
            "params": {
              "handle": "duolingo"
            },
            "inputs": [
              "duolingo",
              "https://www.tiktok.com/@duolingo"
            ],
            "status": 200
          },
          {
            "params": {
              "handle": "@khaby.lame"
            },
            "inputs": [
              "@khaby.lame",
              "khaby.lame",
              "https://www.tiktok.com/@khaby.lame"
            ],
            "status": 200
          },
          {
            "params": {
              "handle": "bts_official_bighit"
            },
            "inputs": [
              "bts_official_bighit",
              "https://www.tiktok.com/@bts_official_bighit"
            ],
            "status": 200
          },
          {
            "params": {
              "handle": "zzq9x81nouser"
            },
            "inputs": [
              "zzq9x81nouser"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "handle": "https://www.tiktok.com/@nasa"
            },
            "inputs": [
              "https://www.tiktok.com/@nasa",
              "nasa"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "handle": "7000000000000000001"
            },
            "inputs": [
              "7000000000000000001"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "handle": "https://www.tiktok.com/share/user/107955"
            },
            "inputs": [
              "https://www.tiktok.com/share/user/107955",
              "107955"
            ],
            "status": 200
          }
        ]
      },
      "tiktok.transcript": {
        "identity": "url",
        "codes": [
          "invalid_input",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "no_transcript",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "url": "https://www.tiktok.com/@scout2015/video/6718335390845095173"
            },
            "inputs": [
              "https://www.tiktok.com/@scout2015/video/6718335390845095173",
              "6718335390845095173"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "7699163285702135073"
            },
            "inputs": [
              "7699163285702135073"
            ],
            "status": 200,
            "shadowed": true
          },
          {
            "params": {
              "lang": "eng-US",
              "url": "https://www.tiktok.com/@elan.media_/video/7663447693913509152"
            },
            "inputs": [
              "https://www.tiktok.com/@elan.media_/video/7663447693913509152",
              "7663447693913509152"
            ],
            "status": 200
          },
          {
            "params": {
              "lang": "de",
              "url": "7689027164263435538"
            },
            "inputs": [
              "7689027164263435538"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.tiktok.com/@niinoclock/video/7673108027645414678"
            },
            "inputs": [
              "https://www.tiktok.com/@niinoclock/video/7673108027645414678",
              "7673108027645414678"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "url": "https://www.tiktok.com/embed/v2/7691047892483656974"
            },
            "inputs": [
              "https://www.tiktok.com/embed/v2/7691047892483656974",
              "7691047892483656974"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.tiktok.com/@elan.media_/video/7663447693913509152?is_from_webapp=1&sender_device=pc"
            },
            "inputs": [
              "https://www.tiktok.com/@elan.media_/video/7663447693913509152?is_from_webapp=1&sender_device=pc",
              "7663447693913509152"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "7699163285702135073"
            },
            "inputs": [
              "7699163285702135073"
            ],
            "status": 200
          },
          {
            "params": {
              "lang": "ja",
              "url": "https://m.tiktok.com/v/7689027164263435538.html"
            },
            "inputs": [
              "https://m.tiktok.com/v/7689027164263435538.html",
              "7689027164263435538"
            ],
            "status": 200
          }
        ]
      },
      "youtube.comments": {
        "identity": "url",
        "codes": [
          "invalid_input",
          "invalid_cursor",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "sort": "popular",
              "url": "HtVdAasjOgU"
            },
            "inputs": [
              "HtVdAasjOgU",
              "https://www.youtube.com/watch?v=HtVdAasjOgU"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "XqZsoesa55w"
            },
            "inputs": [
              "XqZsoesa55w"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "jNQXAC9IVRw"
            },
            "inputs": [
              "jNQXAC9IVRw"
            ],
            "status": 400,
            "error_code": "invalid_cursor",
            "shadowed": true
          },
          {
            "params": {
              "url": "qUTSzYEtUG0"
            },
            "inputs": [
              "qUTSzYEtUG0",
              "https://www.youtube.com/watch?v=qUTSzYEtUG0"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "jNQXAC9IVRw"
            },
            "inputs": [
              "jNQXAC9IVRw",
              "https://www.youtube.com/watch?v=jNQXAC9IVRw"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "since": "2026-10-01T00:00:00Z",
              "url": "dQw4w9WgXcQ"
            },
            "inputs": [
              "dQw4w9WgXcQ",
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            ],
            "status": 200
          },
          {
            "params": {
              "sort": "popular",
              "url": "kJQP7kiw5Fk"
            },
            "inputs": [
              "kJQP7kiw5Fk",
              "https://www.youtube.com/watch?v=kJQP7kiw5Fk"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "Q39EVAstoRM"
            },
            "inputs": [
              "Q39EVAstoRM"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "url": "yZIXLfi8CZQ"
            },
            "inputs": [
              "yZIXLfi8CZQ"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "3cWm9B_0_kI"
            },
            "inputs": [
              "3cWm9B_0_kI",
              "https://www.youtube.com/watch?v=3cWm9B_0_kI"
            ],
            "status": 200
          },
          {
            "params": {
              "sort": "popular",
              "url": "jNQXAC9IVRw"
            },
            "inputs": [
              "jNQXAC9IVRw",
              "https://www.youtube.com/watch?v=jNQXAC9IVRw"
            ],
            "status": 200
          },
          {
            "params": {
              "sort": "popular",
              "url": "dQw4w9WgXcQ"
            },
            "inputs": [
              "dQw4w9WgXcQ",
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "E_HgDSKu_50"
            },
            "inputs": [
              "E_HgDSKu_50",
              "https://www.youtube.com/watch?v=E_HgDSKu_50"
            ],
            "status": 200
          }
        ]
      },
      "youtube.transcript": {
        "identity": "url",
        "codes": [
          "invalid_input",
          "insufficient_credits",
          "spend_limit_reached",
          "rate_limited",
          "not_found",
          "private_or_unavailable",
          "no_transcript",
          "upstream_blocked",
          "upstream_error",
          "overloaded",
          "internal"
        ],
        "cases": [
          {
            "params": {
              "url": "HtVdAasjOgU"
            },
            "inputs": [
              "HtVdAasjOgU"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "https://www.youtube.com/live/qVv6vCqciTM"
            },
            "inputs": [
              "https://www.youtube.com/live/qVv6vCqciTM",
              "qVv6vCqciTM",
              "https://www.youtube.com/watch?v=qVv6vCqciTM"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "dQw4w9WgXcQ"
            },
            "inputs": [
              "dQw4w9WgXcQ"
            ],
            "status": 503,
            "error_code": "upstream_blocked",
            "shadowed": true
          },
          {
            "params": {
              "url": "jNQXAC9IVRw"
            },
            "inputs": [
              "jNQXAC9IVRw",
              "https://www.youtube.com/watch?v=jNQXAC9IVRw"
            ],
            "status": 200
          },
          {
            "params": {
              "lang": "en",
              "url": "dQw4w9WgXcQ"
            },
            "inputs": [
              "dQw4w9WgXcQ",
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            ],
            "status": 200
          },
          {
            "params": {
              "lang": "de",
              "url": "dQw4w9WgXcQ"
            },
            "inputs": [
              "dQw4w9WgXcQ",
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            ],
            "status": 200
          },
          {
            "params": {
              "lang": "ja",
              "url": "https://youtu.be/dQw4w9WgXcQ"
            },
            "inputs": [
              "https://youtu.be/dQw4w9WgXcQ",
              "dQw4w9WgXcQ",
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            ],
            "status": 200
          },
          {
            "params": {
              "lang": "fr",
              "url": "jNQXAC9IVRw"
            },
            "inputs": [
              "jNQXAC9IVRw"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "jfKfPfyJRdk"
            },
            "inputs": [
              "jfKfPfyJRdk"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.youtube.com/watch?v=kCc8FmEb1nY"
            },
            "inputs": [
              "https://www.youtube.com/watch?v=kCc8FmEb1nY",
              "kCc8FmEb1nY"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
            },
            "inputs": [
              "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
              "dQw4w9WgXcQ"
            ],
            "status": 200,
            "default": true
          },
          {
            "params": {
              "lang": "pt-BR",
              "url": "https://www.youtube.com/watch?v=iG9CE55wbtY"
            },
            "inputs": [
              "https://www.youtube.com/watch?v=iG9CE55wbtY",
              "iG9CE55wbtY"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "lqQg6PlCWgI"
            },
            "inputs": [
              "lqQg6PlCWgI"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://www.youtube.com/shorts/tjjjtzRLHvA"
            },
            "inputs": [
              "https://www.youtube.com/shorts/tjjjtzRLHvA",
              "tjjjtzRLHvA"
            ],
            "status": 404,
            "error_code": "no_transcript"
          },
          {
            "params": {
              "url": "https://music.youtube.com/watch?v=kJQP7kiw5Fk"
            },
            "inputs": [
              "https://music.youtube.com/watch?v=kJQP7kiw5Fk",
              "kJQP7kiw5Fk",
              "https://www.youtube.com/watch?v=kJQP7kiw5Fk"
            ],
            "status": 200
          },
          {
            "params": {
              "url": "Q39EVAstoRM"
            },
            "inputs": [
              "Q39EVAstoRM"
            ],
            "status": 404,
            "error_code": "not_found"
          },
          {
            "params": {
              "url": "yYr8q0y5Jfg"
            },
            "inputs": [
              "yYr8q0y5Jfg"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "yZIXLfi8CZQ"
            },
            "inputs": [
              "yZIXLfi8CZQ"
            ],
            "status": 403,
            "error_code": "private_or_unavailable"
          },
          {
            "params": {
              "url": "6SJNVb0GnPI"
            },
            "inputs": [
              "6SJNVb0GnPI"
            ],
            "status": 404,
            "error_code": "not_found"
          }
        ]
      }
    }
  },
  "stats_meta": {
    "generated_at": "2026-10-25T23:03:48Z",
    "environment": "staging",
    "window_days": 7,
    "method": "Gateway requests in staging of kind api/canary over the last 7 days, excluding cache hits and requests rejected before the engine (unauthorized, insufficient_credits, rate_limited); success_rate counts every outcome except upstream_blocked, upstream_error, internal, canary_fail as success (correct invalid_input, not_found, private_or_unavailable or no_transcript answers succeed), p50/p95 are gateway latency over HTTP 200 responses weighted by sample interval, and endpoints with fewer than 20 requests are omitted."
  },
  "event_types": [
    {
      "type": "job.completed",
      "doc": "A job ended with every item ended (check request_counts: items fail individually).",
      "data_type": "JobEventData"
    },
    {
      "type": "job.canceled",
      "doc": "A canceled job ended: its running items finished.",
      "data_type": "JobEventData"
    },
    {
      "type": "job.expired",
      "doc": "A job reached its deadline; its unfinished items expired.",
      "data_type": "JobEventData"
    },
    {
      "type": "credits.low",
      "doc": "A balance change took the balance from above the account's low_balance_threshold to at or below it (once per crossing; the owner is emailed too).",
      "data_type": "CreditsLowData"
    },
    {
      "type": "credits.depleted",
      "doc": "The balance reached 0 (once, until credits are added).",
      "data_type": "CreditsDepletedData"
    },
    {
      "type": "api_key.spend_limit_reached",
      "doc": "An API key's request (or job) was refused for its spend limit, the first time in the limit's interval.",
      "data_type": "SpendLimitReachedData"
    },
    {
      "type": "webhook.test",
      "doc": "Sent by POST /v1/webhook_endpoints/{id}/test to that endpoint only.",
      "data_type": "WebhookTestData"
    }
  ],
  "jobs": {
    "max_requests": 1000,
    "max_wait_s": 60,
    "max_pages_per_request": 1000,
    "max_pages": 10000,
    "queued_max_pages": 50000,
    "deadline_s": 86400,
    "results_retention_s": 604800,
    "records_retention_s": 2592000,
    "results_url_ttl_s": 3600,
    "concurrency": 10,
    "account_concurrency": 25,
    "max_attempts": 3,
    "metadata_max_keys": 16,
    "metadata_key_max_length": 40,
    "metadata_value_max_length": 500,
    "custom_id_max_length": 64,
    "custom_id_pattern": "^[A-Za-z0-9_-]{1,64}$",
    "results_page_max": 100,
    "prefer_async": "respond-async",
    "async_custom_id": "request",
    "job_id_prefix": "job_",
    "results_download_format": "jsonl",
    "ended_statuses": [
      "completed",
      "canceled",
      "expired"
    ]
  },
  "webhooks": {
    "retry_schedule_s": [
      0,
      5,
      300,
      1800,
      7200,
      18000,
      36000,
      50400,
      72000,
      86400
    ],
    "retry_jitter": 0.1,
    "timeout_s": 15,
    "rotation_overlap_max_s": 86400,
    "secret_prefix": "whsec_",
    "secret_bytes": 32,
    "signature_version": "v1",
    "tolerance_s": 300,
    "disable_after_failing_s": 432000,
    "response_excerpt_bytes": 1024,
    "events_retention_s": 2592000,
    "max_endpoints": 16,
    "user_agent": "SocialTap-Webhooks/1",
    "endpoint_id_prefix": "we_",
    "event_id_prefix": "evt_",
    "delivery_id_prefix": "whd_",
    "content_type": "application/json"
  },
  "idempotency": {
    "key_max_length": 255,
    "ttl_s": 86400,
    "in_progress_s": 120,
    "retry_after_s": 1
  }
}
