Post schema
The unified SocialCrawl post schema. Every field, its type, per-platform availability, and a machine-readable JSON Schema, generated from the canonical Zod source.
Post schema
Every SocialCrawl endpoint that returns a post gives you this exact shape, whatever the source platform. Write your parser once and the same code reads post data from every platform below. That is the unified schema: one contract instead of a dozen raw upstream formats.
Field reference
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string | No | Platform-specific post ID (always a string; numeric upstream IDs are stringified) |
url | string | Yes | Direct URL to the post on the source platform |
content.text | string | Yes | Post caption, description, or text content |
content.media_urls | string or string[] | Yes | URL(s) of the primary media. Single string for video/photo posts; array of strings for carousels. |
content.thumbnail_url | string | Yes | URL to thumbnail image |
content.duration_seconds | integer | Yes | Video/clip duration in seconds (null for non-video posts) |
author.username | string | Yes | Author username |
author.display_name | string | Yes | Author display name |
author.avatar_url | string | Yes | URL to author profile picture |
author.verified | boolean | Yes | Whether the author account is verified |
engagement.views | integer | Yes | View count (if available). For Instagram video/reels this is the play count (video_play_count) — the headline 'Views' the IG app shows; the legacy 3-second-view count is preserved separately under post.ext.video_view_count on /v1/instagram/post. |
engagement.likes | integer | Yes | Like / reaction count |
engagement.comments | integer | Yes | Comment count. Note: on GET /v1/instagram/post this is Instagram's edge_media_to_parent_comment.count (top-level comments); the IG list endpoints report the mobile comment_count total (includes replies). Both land on this same field. |
engagement.shares | integer | Yes | Share / repost / retweet count |
engagement.saves | integer | Yes | Save / bookmark count. null on Instagram — the save count is platform-private and Instagram exposes no numeric save metric on any surface (never fabricated). |
flags.nsfw | boolean | Yes | NSFW flag (null when platform does not surface) |
flags.spoiler | boolean | Yes | Spoiler flag (null when platform does not surface) |
flags.pinned | boolean | Yes | Pinned-to-profile flag (null when platform does not surface) |
flags.deleted | boolean | No | Whether the post is tombstoned (always present) |
flags.likes_hidden | boolean | Yes | Present and true when the creator has hidden the like count. engagement.likes is null in that case (never the platform's decoy preview number). Absent on normal posts. |
flags.comments_hidden | boolean | Yes | Present and true when the upstream reports the comment count as hidden. engagement.comments is null in that case. Absent on normal posts. |
flags.shares_hidden | boolean | Yes | |
flags.views_hidden | boolean | Yes | |
flags.saves_hidden | boolean | Yes | |
published_at | string or integer | Yes | Post creation timestamp as an ISO 8601 UTC string. When the upstream sent a Unix epoch it is converted here and the raw epoch is preserved under post.ext.published_at_epoch for one deprecation cycle. |
Extension fields (ext)
Platform-specific passthrough. Each field is present only on the platforms that expose it and is absent everywhere else, so treat every ext.* field as optional. These carry the richer, per-platform signals the unified leaves cannot hold, and the join keys that chain endpoints together.
| Field | Type | Nullable | Description |
|---|---|---|---|
ext.music_id | string | Yes | TikTok music/clip id (exact string) — pass to /v1/tiktok/song/videos?clipId= to find videos using the same sound |
ext.subreddit | string | Yes | Reddit subreddit name (search/list items) — pass to /v1/reddit/subreddit/details?subreddit= |
ext.type | string | Yes | |
ext.content_type | string | Yes | |
ext.ticker_symbols | string[] | Yes | |
ext.video_view_count | integer | Yes | Instagram reel legacy 3-second-view count. engagement.views carries the play count (the headline 'Views' the IG app shows); this preserves the older view count. Present only on /v1/instagram/post for video posts. |
ext.published_at_epoch | integer | Yes | Raw Unix epoch for published_at (seconds, or milliseconds when the upstream sent millis). Present only when the upstream sent a numeric epoch that was normalised to the ISO 8601 published_at string. Kept for one deprecation cycle for integrations pinned to the numeric form. |
ext.usertags | string[] | Yes | |
ext.coauthors | string[] | Yes | |
ext.music | string | Yes | |
ext.sponsor_tags | string[] | Yes | |
ext.location | string | Yes | |
ext.reaction_counts | string[] | Yes | |
ext.share_urn | string | Yes | |
ext.post_type | string | Yes | |
ext.download_media_urls | string[] | Yes | |
ext.tags | string[] | Yes | |
ext.categoryId | string | Yes | |
ext.categoryTitle | string | Yes | |
ext.topicCategories | string[] | Yes | |
ext.duration | string | Yes | |
ext.license | string | Yes | |
ext.madeForKids | boolean | Yes | |
ext.defaultAudioLanguage | string | Yes | |
ext.hasPaidProductPlacement | boolean | Yes | |
ext.caption | string | Yes | |
ext.position | integer | Yes | |
ext.playlistId | string | Yes | |
ext.videoOwnerChannelId | string | Yes | |
ext.videoPublishedAt | string | Yes | |
ext.description | string | Yes | |
ext.default_language | string | Yes | |
ext.playlist_item_id | string | Yes | |
ext.playlist_owner_channel_id | string | Yes | |
ext.playlist_owner_title | string | Yes |
Platform availability
22 platforms return the Post shape. id, flags.deleted are never null on any platform. The fields below vary: yes means the platform populates it (the value may still be null); a blank means the platform never provides it, so it is always null.
| Platform | author.verified | engagement.saves | content.duration_seconds | engagement.shares | engagement.views | url | author.avatar_url | engagement.comments | content.media_urls | content.thumbnail_url | engagement.likes | author.username |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Amazon | yes | yes | yes | yes | ||||||||
| Bluesky | yes | yes | yes | yes | yes | |||||||
| yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| GitHub | yes | yes | yes | yes | yes | yes | yes | |||||
| yes | yes | yes | yes | |||||||||
| Hacker News | yes | yes | yes | yes | ||||||||
| yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| Kick | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| Kwai | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes |
| yes | yes | yes | ||||||||||
| Naver | yes | yes | yes | |||||||||
| yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| Rumble | yes | yes | yes | yes | yes | yes | ||||||
| Spotify | yes | yes | yes | yes | yes | yes | ||||||
| Threads | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| TikTok | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes |
| TikTok Shop | yes | yes | yes | yes | yes | |||||||
| Truth Social | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes |
| Twitch | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | |
| Twitter/X | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes |
| YouTube | yes | yes | yes | yes | yes | yes | yes | yes | yes |
Machine-readable schema
Validate responses programmatically against the JSON Schema (2020-12):
Point a validator (Ajv, jsonschema, or your framework's) at that URL, or hand it to an agent so it can check the shape without a live call.
Returned by
Endpoints with the Post / PostList archetype return this shape:
Amazon · Bluesky · Facebook · GitHub · Google · Hacker News · Instagram · Kick · Kwai · LinkedIn · Naver · Pinterest · Reddit · Rumble · Spotify · Threads · TikTok · TikTok Shop · Truth Social · Twitch · Twitter/X · YouTube
See how the same fields map to each platform's raw upstream names in the cross-platform field equivalence table.
