API Reference

Introduction

The ScrapingIsNotACrime API lets you retrieve public data from Instagram, TikTok, YouTube, the Apple App Store, GitHub, Hacker News, Bluesky, Twitch, and Linktree via simple HTTP requests — X (Twitter) is coming soon. Every endpoint returns clean, typed JSON.

Base URLhttps://api.scrapingisnotacrime.com/v1
Public data only. Every endpoint targets publicly accessible content — profiles, posts, and metadata visible without logging in. We never bypass authentication or access private accounts.

Authentication

All requests must include your API key in the X-Api-Key header. Keys look like hob_ followed by 64 hex characters, and you generate them from the dashboard. The key is the only header you need — it resolves your account and workspace on its own, so there is nothing else to send.

bashexample
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/cristiano" \
  -H "X-Api-Key: hob_your_api_key"
Keep it secret. Never expose your API key in client-side code or public repos. Rotate it from the dashboard immediately if compromised.

Quota

Each successful request draws from your workspace's scraping quota for the current billing period. Once it runs out, every call answers 402 with plan.quota_exceeded until the period rolls over. Your remaining balance is not carried in the response — check it from the dashboard.

Every endpoint answers with the same two-key envelope:

responseevery endpoint
{
  "message": "Profile retrieved successfully.",
  "data": "{ ... }"
}
Response language. Send X-Language with en-US, pt-BR, or es-ES to pick the language of the message field. It defaults to en-US, and the locale actually applied comes back in the X-Language response header.

Errors

The API uses standard HTTP status codes. An error drops the data key and answers with message alone — so branch on the status code, not on the text, which is prose and is translated by X-Language.

error response
{
  "message": "The user @notfound does not exist or could not be found."
}
StatusExample messageMeaning
200Success
400Invalid username format.Parameter missing or malformed. The wording names the parameter at fault — Invalid shortcode format., and so on.
401Invalid X-Api-Key.API key missing or not recognized. Also returned when a JWT session or workspace is invalid.
402plan.quota_exceededThe workspace has used up its scraping quota for the current period.
404The user @notfound does not exist or could not be found.Profile or content not found on the source platform.
429Instagram rate limit reached. Please try again later.The source platform is rate limiting us. The message names whichever platform it was.
502Instagram could not be reached after multiple attempts.Source platform unreachable after retries.

Instagram

Access public Instagram profiles, contact blocks, timelines, highlights, posts, reels, media download links, and shortcode/media_id conversion. All endpoints target publicly visible content only.

GET/instagram/profile/{username}

Returns public profile metadata for any Instagram username — follower count, following, media count, bio and bio links, verification and privacy flags, and the business block. Note the post count arrives as medias, and the profile's own capability flags (has_clips, has_guides, and friends) come along with it.

Parameters

NameTypeInDescription
usernamestringpathInstagram username without @
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/instagram" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Profile retrieved successfully.",
  "data": {
    "id": "25025320",
    "fbid": "17841400455970167",
    "username": "instagram",
    "full_name": "Instagram",
    "bio": "The Instagram account — connecting the world through photos, videos and messages.",
    "bio_links": [
      "https://about.instagram.com/"
    ],
    "followers": 678000000,
    "following": 500,
    "medias": 1300,
    "highlight_reel_count": 10,
    "profile_pic": "https://scontent.example.com/v/profile.jpg",
    "has_ar_effects": true,
    "has_clips": true,
    "has_guides": false,
    "has_channel": false,
    "has_blocked_viewer": false,
    "is_business_account": true,
    "business_address_json": null,
    "business_contact_method": "CALL",
    "business_email": null,
    "business_phone_number": null,
    "business_category_name": "Internet company",
    "is_professional_account": true,
    "category_name": "Internet company",
    "is_private": false,
    "is_verified": true
  }
}
GET/instagram/profile/{username}/contact

Returns the public contact block of a profile — the structured business email, phone, and address Instagram exposes, plus any email addresses and phone numbers written into the bio itself. Built from the same payload as the profile endpoint, so it costs no extra round trip to Instagram. A profile with no contact information returns 200 with the fields set to null.

Parameters

NameTypeInDescription
usernamestringpathInstagram username without @
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/cafedaesquina/contact" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Contact retrieved successfully.",
  "data": {
    "username": "cafedaesquina",
    "full_name": "Café da Esquina",
    "biography": "☕ Pedidos: [email protected] | wa.me/5511999990000",
    "is_verified": false,
    "is_business": true,
    "category": "Coffee shop",
    "email": null,
    "phone": null,
    "external_url": "https://cafe.com.br",
    "address": {
      "street_address": "Rua X, 123",
      "zip_code": "01310-100",
      "city_name": "São Paulo",
      "region_name": "SP",
      "country_code": "BR"
    },
    "emails_found": [
      {
        "value": "[email protected]",
        "source": "biography"
      }
    ],
    "phones_found": [
      {
        "value": "+5511999990000",
        "source": "whatsapp_link"
      }
    ]
  }
}
GET/instagram/profile/{username}/timeline/latest

Returns the first page of a profile's public posts — roughly the 12 most recent. count is the profile's lifetime post total, latest_count is how many came back in this response. Video entries carry extra fields the image ones lack: video_views, video_url, has_audio, and clips_music_attribution_info. For anything past the first page, use /timeline.

Parameters

NameTypeInDescription
usernamestringpathInstagram username without @
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/instagram/timeline/latest" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Timeline retrieved successfully.",
  "data": {
    "count": 1300,
    "latest_count": 12,
    "medias": [
      {
        "id": "3123456789012345678",
        "shortcode": "CxBcDefGhIj",
        "type": "image",
        "comments": 150,
        "likes": 12500,
        "caption": "A beautiful sunset 🌅 #nature #photography",
        "location": null,
        "thumbnail_resources": null,
        "display_url": "https://scontent.example.com/v/image.jpg",
        "taken_at_timestamp": "2024-04-01 18:30:00 UTC"
      },
      {
        "id": "3198765432109876543",
        "shortcode": "DyKlMnOpQrS",
        "type": "video",
        "video_views": 2500000,
        "comments": 320,
        "likes": 48000,
        "caption": "Behind the scenes 🎬 #instagram",
        "location": null,
        "thumbnail_resources": null,
        "display_url": "https://scontent.example.com/v/thumb.jpg",
        "video_url": "https://scontent.example.com/v/video.mp4",
        "has_audio": true,
        "clips_music_attribution_info": null,
        "taken_at_timestamp": "2024-03-28 14:00:00 UTC"
      }
    ]
  }
}
GET/instagram/profile/{username}/timeline

Walks a profile's full post history page by page. Pass the previous response's next_cursor back as cursor to advance; has_more tells you when there is nothing left. Use this instead of /timeline/latest whenever you need more than the first page.

Parameters

NameTypeInDescription
usernamestringpathInstagram username without @
countintegerquery·optPosts per page, 1–50. Default 12. This is a request, not a guarantee — Instagram may return fewer.
cursorstringquery·optCursor from the previous response's next_cursor. Omit it to fetch the first page.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/nasa/timeline?count=12&cursor=3950671748375397992_528817151" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Timeline retrieved successfully.",
  "data": {
    "medias": [
      {
        "id": "3957840214710558089",
        "shortcode": "DbtErSrlB2J",
        "type": "image",
        "caption": "A beautiful sunset 🌅",
        "likes": 12500,
        "comments": 150,
        "preview_comments": [],
        "location": null,
        "display_url": "https://...",
        "taken_at_timestamp": "2026-08-05 18:30:00 UTC"
      }
    ],
    "has_more": true,
    "next_cursor": "3943186559128294411_528817151"
  }
}
GET/instagram/profile/{username}/highlights

Lists the highlight reels pinned to a profile — id, title, and cover image for each one. Feed the returned id into /instagram/highlights/{highlightId} to read the stories inside. Note this endpoint costs two calls to Instagram internally: the highlights query needs a numeric user_id, and only the profile endpoint can turn a username into one.

Parameters

NameTypeInDescription
usernamestringpathInstagram username without @
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/nasa/highlights" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Highlights retrieved successfully.",
  "data": {
    "username": "nasa",
    "user_id": "528817151",
    "highlights": [
      {
        "id": "highlight:18201653992314974",
        "title": "Artemis III",
        "cover": "https://..."
      }
    ]
  }
}
GET/instagram/highlights/{highlightId}

Returns the stories inside a highlight, in the same media shape used by the timeline endpoints. Stories carry no public like or comment counts, so those fields always come back as 0 — that is Instagram's behaviour, not a gap in the data.

Parameters

NameTypeInDescription
highlightIdstringpathHighlight id from the highlights listing, including the highlight: prefix
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/highlights/highlight:18201653992314974" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Highlight retrieved successfully.",
  "data": {
    "id": "highlight:18201653992314974",
    "title": "Artemis III",
    "items": [
      {
        "id": "3914477988573951523",
        "shortcode": "CxBcDefGhIj",
        "type": "image",
        "caption": "",
        "likes": 0,
        "comments": 0,
        "preview_comments": [],
        "location": null,
        "display_url": "https://...",
        "taken_at_timestamp": "2026-06-08 12:36:27 UTC"
      }
    ]
  }
}
GET/instagram/profile/{username}/media/{mediaId}

Returns full details for a specific media item identified by its numeric ID, scoped to the profile — media type, caption, engagement metrics, and the display URL. Same media object the timeline endpoints return.

Parameters

NameTypeInDescription
usernamestringpathInstagram username without @
mediaIdstringpathNumeric media ID (e.g. 3123456789012345678)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/instagram/media/3123456789012345678" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Media retrieved successfully.",
  "data": {
    "id": "3123456789012345678",
    "shortcode": "CxBcDefGhIj",
    "type": "image",
    "comments": 150,
    "likes": 12500,
    "caption": "A beautiful sunset 🌅 #nature #photography",
    "location": null,
    "thumbnail_resources": null,
    "display_url": "https://scontent.example.com/v/image.jpg",
    "taken_at_timestamp": "2024-04-01 18:30:00 UTC"
  }
}
GET/instagram/media/{shortcode}

Fetches a media item directly by its shortcode — the alphanumeric code found in Instagram post URLs. Supports single images, videos, and carousel/sidecar posts.

Parameters

NameTypeInDescription
shortcodestringpathPost shortcode (e.g. C8xQz1aP9Kv from instagram.com/p/C8xQz1aP9Kv/)
GET/instagram/media/{shortcode}/download

Returns a flat list of every downloadable asset behind a post, reel, or carousel — video files, images, and thumbnails — each with its resolution and expiry. The list is ordered so that assets[0] is always the best primary asset; on a carousel, every asset carries the index of the child it belongs to. The response holds Meta CDN links, not the bytes: fetch them directly from the CDN before expires_at. An existing post with no downloadable asset returns an empty assets array, not a 404.

Parameters

NameTypeInDescription
shortcodestringpathPost, reel, or carousel shortcode (e.g. DbtErSrlB2J)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/media/DbtErSrlB2J/download" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Download links retrieved successfully.",
  "data": {
    "shortcode": "DbtErSrlB2J",
    "type": "video",
    "expires_at": "2026-08-08 07:51:36 UTC",
    "assets": [
      {
        "kind": "video",
        "index": 0,
        "url": "https://instagram.fxxx.fbcdn.net/o1/v/t2/f2/m86/video.mp4",
        "width": 720,
        "height": 1280,
        "quality": "101",
        "expires_at": "2026-08-08 07:51:36 UTC"
      },
      {
        "kind": "thumbnail",
        "index": 0,
        "url": "https://instagram.fxxx.fbcdn.net/v/t51/thumb.jpg",
        "width": 1080,
        "height": 1920,
        "quality": null,
        "expires_at": "2026-08-08 07:51:36 UTC"
      }
    ]
  }
}
GET/instagram/media/{shortcode}/id

Converts a shortcode into its numeric media_id. A shortcode is just the media_id encoded in base64 over the A-Za-z0-9-_ alphabet, so this runs locally — no call reaches Instagram and nothing can rate-limit it.

Parameters

NameTypeInDescription
shortcodestringpathPost shortcode (e.g. Dbn-XJhk0_-)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/media/Dbn-XJhk0_-/id" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Converted successfully.",
  "data": {
    "shortcode": "Dbn-XJhk0_-",
    "media_id": "3956405067326902270"
  }
}
GET/instagram/media/id/{mediaId}

The inverse conversion: numeric media_id to shortcode, also computed locally. Accepts both the bare form (123) and the owner-suffixed form (123_456) that Instagram payloads often carry, so you can pass an id straight through from another response.

Parameters

NameTypeInDescription
mediaIdstringpathNumeric media id, with or without the _ownerId suffix
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/media/id/3956405067326902270" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Converted successfully.",
  "data": {
    "media_id": "3956405067326902270",
    "shortcode": "Dbn-XJhk0_-"
  }
}
GET/instagram/reels/{shortcode}

Returns reel data by shortcode — view count, like and comment counts, the video and cover URLs, and music attribution. It is the same media object the other Instagram endpoints return, so the view count is video_views and the cover is display_url. clips_music_attribution_info carries the track when there is one, and comes back null for original audio.

Parameters

NameTypeInDescription
shortcodestringpathReel shortcode (e.g. DyKlMnOpQrS from instagram.com/reels/DyKlMnOpQrS/)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/instagram/reels/DyKlMnOpQrS" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Media retrieved successfully.",
  "data": {
    "id": "3198765432109876543",
    "shortcode": "DyKlMnOpQrS",
    "type": "video",
    "video_views": 2500000,
    "comments": 320,
    "likes": 48000,
    "caption": "Behind the scenes 🎬 #instagram #reels",
    "location": null,
    "thumbnail_resources": null,
    "display_url": "https://scontent.example.com/v/thumb.jpg",
    "video_url": "https://scontent.example.com/v/reel.mp4",
    "has_audio": true,
    "clips_music_attribution_info": {
      "artist_name": "Artist Name",
      "song_name": "Song Title",
      "uses_original_audio": false
    },
    "taken_at_timestamp": "2024-03-28 14:00:00 UTC"
  }
}

TikTok

Access public TikTok profiles and individual videos.

GET/tiktok/profile/{username}

Returns public profile metadata — follower count, video count, bio and bio link, avatar, and verification status. Total likes across the account come back as hearts, and sec_uid is TikTok's stable internal identifier for the user.

Parameters

NameTypeInDescription
usernamestringpathTikTok username without @
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/tiktok/profile/tiktok" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Profile retrieved successfully.",
  "data": {
    "id": "107955",
    "username": "tiktok",
    "nickname": "TikTok",
    "bio": "One TikTok can make a big impact",
    "bio_link": "linktr.ee/tiktok",
    "avatar": "https://p16-common-sign.tiktokcdn.com/...",
    "sec_uid": "MS4wLjABAAAAv7iSuuXDJGDvJkmH_vz1qkDZYo1apxgzaxdBSeIuPiM",
    "followers": 94800000,
    "following": 0,
    "hearts": 461700000,
    "videos": 1468,
    "is_private": false,
    "is_verified": true
  }
}
GET/tiktok/video/{videoId}

Returns full details for a single public TikTok video — view/like/share/comment counts, duration, cover image, and audio information.

Parameters

NameTypeInDescription
videoIdstringpathTikTok numeric video ID

YouTube

List a channel's public videos by handle.

GET/youtube/channel/{handle}/videos

Returns a channel's public videos alongside the channel block itself — title, description, external id, and avatar. Views and age are not separate numeric fields: YouTube renders them as display strings, so they arrive together in metadataText (for example ["20K views", "2 days ago"]). Parse that array if you need them as numbers.

Parameters

NameTypeInDescription
handlestringpathChannel handle without @ (e.g. youtube)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/youtube/channel/youtube/videos" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Channel videos retrieved successfully.",
  "data": {
    "channel": {
      "title": "YouTube",
      "description": "Official YouTube channel",
      "externalId": "UCBR8-60-B28hp2BmDPdntcQ",
      "avatar": "https://yt3.googleusercontent.com/..."
    },
    "videos": [
      {
        "id": "dQw4w9WgXcQ",
        "title": "Sample video title",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
        "metadataText": [
          "20K views",
          "2 days ago"
        ]
      }
    ]
  }
}

App Store

Search public apps and read customer reviews from the Apple App Store.

GET/appstore/reviews

Returns customer reviews for a public app, sorted by most recent. Apple's own feed caps at 10 pages (roughly 500 reviews) — requesting further pages returns a 400.

Parameters

NameTypeInDescription
appIdstringquery·reqNumeric App Store app ID (the id field from a search result)
countrystringquery·opt2-letter country code (ISO 3166-1 alpha-2). Default us.
pageintegerquery·optReview page, 1–10. Default 1.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/appstore/reviews?appId=389801252&country=us&page=1" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "App reviews retrieved successfully.",
  "data": {
    "appId": "389801252",
    "country": "us",
    "page": 1,
    "reviews": [
      {
        "id": "14288247378",
        "author": "exampleuser",
        "rating": 4,
        "title": "Great app overall",
        "content": "Does what it says, minor bugs here and there.",
        "version": "437.2.0",
        "updatedAt": "2026-07-10T18:19:36-07:00",
        "voteCount": 3,
        "voteSum": 2
      }
    ]
  }
}

GitHub

Public GitHub profiles, follower graphs, repositories, and repository search. Collection endpoints page with limit and page; total comes back null on them because GitHub's REST API does not report a count for collections, and has_more is inferred from the page arriving full.

GET/github/profiles/{handle}

Returns the public profile of a GitHub user — name, bio, company, location, blog, public repository count, and follower totals.

Parameters

NameTypeInDescription
handlestringpathGitHub username (e.g. torvalds)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/github/profiles/torvalds" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Profile retrieved successfully.",
  "data": {
    "username": "torvalds",
    "id": 1024025,
    "name": "Linus Torvalds",
    "bio": null,
    "company": "Linux Foundation",
    "location": "Portland",
    "blog": "https://kernel.org",
    "public_repos": 8,
    "followers": 315540,
    "following": 0,
    "avatar": "https://avatars.githubusercontent.com/u/1024025",
    "created_at": "2011-09-03T15:26:22Z",
    "url": "https://github.com/torvalds"
  }
}
GET/github/profiles/{handle}/followers

Lists the accounts following a user, one page at a time.

Parameters

NameTypeInDescription
handlestringpathGitHub username
limitintegerquery·optResults per page, 1–100. Default 30.
pageintegerquery·optPage number, 1-based. Default 1.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/github/profiles/torvalds/followers?limit=30&page=1" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Followers retrieved successfully.",
  "data": {
    "items": [
      {
        "username": "sprsquish",
        "id": 1,
        "avatar": "https://avatars.githubusercontent.com/u/1",
        "url": "https://github.com/sprsquish"
      }
    ],
    "total": null,
    "has_more": true
  }
}
GET/github/profiles/{handle}/following

Lists the accounts a user follows. Same page shape and same user object as the followers endpoint.

Parameters

NameTypeInDescription
handlestringpathGitHub username
limitintegerquery·optResults per page, 1–100. Default 30.
pageintegerquery·optPage number, 1-based. Default 1.
GET/github/profiles/{handle}/repositories

Lists a user's public repositories, ordered by star count descending.

Parameters

NameTypeInDescription
handlestringpathGitHub username
limitintegerquery·optResults per page, 1–100. Default 30.
pageintegerquery·optPage number, 1-based. Default 1.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/github/profiles/torvalds/repositories?limit=30" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Repositories retrieved successfully.",
  "data": {
    "items": [
      {
        "name": "linux",
        "full_name": "torvalds/linux",
        "description": "Linux kernel source tree",
        "stars": 180000,
        "forks": 53000,
        "language": "C",
        "topics": [],
        "is_fork": false,
        "is_archived": false,
        "created_at": "2011-09-04T22:48:12Z",
        "updated_at": "2026-01-01T00:00:00Z",
        "url": "https://github.com/torvalds/linux"
      }
    ],
    "total": null,
    "has_more": false
  }
}
GET/github/repositories

Searches public repositories. The q parameter takes GitHub's own search syntax verbatim, so qualifiers like stars:>10000, language:php, or pushed:>2026-01-01 all work. Unlike the per-user listings, this one returns a real total and echoes the page back.

Parameters

NameTypeInDescription
qstringquery·reqSearch query in GitHub syntax (e.g. stars:>10000 language:php)
limitintegerquery·optResults per page, 1–100. Default 30.
pageintegerquery·optPage number, 1-based. Default 1.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/github/repositories?q=stars:%3E10000+language:php&limit=1" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Repositories retrieved successfully.",
  "data": {
    "items": [
      {
        "name": "laravel",
        "full_name": "laravel/laravel",
        "description": "Laravel is a web application framework with expressive, elegant syntax.",
        "stars": 79000,
        "forks": 24000,
        "language": "PHP",
        "topics": [
          "php",
          "framework",
          "laravel"
        ],
        "is_fork": false,
        "is_archived": false,
        "created_at": "2011-06-08T17:36:00Z",
        "updated_at": "2026-08-09T11:20:00Z",
        "url": "https://github.com/laravel/laravel"
      }
    ],
    "total": 5000,
    "page": 1,
    "has_more": true
  }
}

Hacker News

Feeds, full comment threads, search, and user activity from Hacker News. Paging here is 0-based — page starts at 0, not 1, unlike the GitHub endpoints.

GET/hackernews/feeds/{feed}

Returns one of the site's feeds as a page of stories.

Parameters

NameTypeInDescription
feedstringpathOne of top, new, best, ask, show, job
limitintegerquery·optStories per page, 1–50. Default 20.
pageintegerquery·optPage number, 0-based. Default 0.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/hackernews/feeds/top?limit=20&page=0" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Feed retrieved successfully.",
  "data": {
    "items": [
      {
        "id": 41203344,
        "title": "Postgres 18 is out",
        "author": "pg_fan",
        "points": 842,
        "num_comments": 317,
        "url": "https://www.postgresql.org/about/news/",
        "text": null,
        "created_at": "2026-08-10T13:02:11.000Z",
        "hn_url": "https://news.ycombinator.com/item?id=41203344"
      }
    ],
    "total": 300,
    "page": 0,
    "has_more": true
  }
}
GET/hackernews/items/{id}

Returns a single item — story, comment, job, or poll — together with its full comment tree. Replies nest inside each comment's replies array, so the thread arrives in one response instead of one call per node.

Parameters

NameTypeInDescription
idintegerpathHacker News item id (e.g. 8863)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/hackernews/items/8863" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Item retrieved successfully.",
  "data": {
    "id": 8863,
    "type": "story",
    "title": "My YC app: Dropbox",
    "author": "dhouston",
    "points": 111,
    "url": "http://getdropbox.com",
    "text": null,
    "created_at": "2007-04-04T19:16:40.000Z",
    "hn_url": "https://news.ycombinator.com/item?id=8863",
    "comments": [
      {
        "id": 8952,
        "author": "BrandonM",
        "text": "I have a few qualms with this app...",
        "created_at": "2007-04-04T19:20:00.000Z",
        "replies": []
      }
    ]
  }
}
GET/hackernews/users/{username}

Returns a user's public profile — karma, about text, account creation date, and total submission count.

Parameters

NameTypeInDescription
usernamestringpathHacker News username (e.g. pg)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/hackernews/users/pg" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "User retrieved successfully.",
  "data": {
    "username": "pg",
    "karma": 157316,
    "about": "Bio text as rendered HTML",
    "created_at": "2006-10-09 18:21:32 UTC",
    "submission_count": 1234,
    "hn_url": "https://news.ycombinator.com/user?id=pg"
  }
}
GET/hackernews/users/{username}/submissions

Lists the stories a user submitted, newest first.

Parameters

NameTypeInDescription
usernamestringpathHacker News username
limitintegerquery·optResults per page, 1–50. Default 20.
pageintegerquery·optPage number, 0-based. Default 0.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/hackernews/users/pg/submissions?limit=20&page=0" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Submissions retrieved successfully.",
  "data": {
    "items": [
      {
        "id": 123,
        "title": "A post",
        "author": "pg",
        "points": 42,
        "num_comments": 7,
        "url": "https://example.test",
        "text": null,
        "created_at": "2026-01-01T00:00:00.000Z",
        "hn_url": "https://news.ycombinator.com/item?id=123"
      }
    ],
    "total": 1234,
    "page": 0,
    "has_more": true
  }
}
GET/hackernews/users/{username}/comments

Lists the comments a user posted, newest first. Same page envelope as the other listings — items, total, page, has_more.

Parameters

NameTypeInDescription
usernamestringpathHacker News username
limitintegerquery·optResults per page, 1–50. Default 20.
pageintegerquery·optPage number, 0-based. Default 0.

Bluesky

Public Bluesky profiles and posts, addressed by full handle.

GET/bluesky/profiles/{handle}

Returns a public profile — DID, display name, description, avatar and banner, follower and post counts.

Parameters

NameTypeInDescription
handlestringpathFull handle, including the domain (e.g. bsky.app or user.bsky.social)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/bluesky/profiles/bsky.app" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Profile retrieved successfully.",
  "data": {
    "did": "did:plc:z72i7hdynmk6r22z27h6tvur",
    "handle": "bsky.app",
    "display_name": "Bluesky",
    "description": "official Bluesky account",
    "avatar": "https://cdn.bsky.app/img/avatar/plain/a.jpg",
    "banner": null,
    "followers": 34441055,
    "following": 11,
    "posts": 804,
    "created_at": "2023-04-12T04:53:57.057Z",
    "url": "https://bsky.app/profile/bsky.app"
  }
}
GET/bluesky/profiles/{handle}/posts

Lists a profile's posts, paginated by the AT Protocol's opaque cursor. Pass the previous response's next_cursor back as cursor to advance; has_more tells you when the feed is exhausted. Each post carries its at:// URI and CID alongside the engagement counts.

Parameters

NameTypeInDescription
handlestringpathFull handle, including the domain
limitintegerquery·optPosts per page, 1–100. Default 25.
cursorstringquery·optCursor from the previous response's next_cursor. Omit for the first page.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/bluesky/profiles/bsky.app/posts?limit=25" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Posts retrieved successfully.",
  "data": {
    "posts": [
      {
        "uri": "at://did:plc:z72i/app.bsky.feed.post/3l7abc",
        "cid": "bafy",
        "text": "We are rolling out improvements",
        "author": "bsky.app",
        "likes": 2543,
        "reposts": 235,
        "replies": 247,
        "quotes": 83,
        "created_at": "2026-08-05T20:32:40.425Z",
        "indexed_at": "2026-08-05T20:33:00.000Z",
        "url": "https://bsky.app/profile/bsky.app/post/3l7abc"
      }
    ],
    "next_cursor": "2026-07-10T17:43:30.972Z",
    "has_more": true
  }
}

Twitch

Public Twitch channel data — profile, live status, and published videos.

GET/twitch/profiles/{handle}

Returns a channel's profile in a single call: follower count, partner and affiliate status, whether it is live right now with the current viewer count, and the last broadcast's title and start time. live_viewers is null whenever is_live is false.

Parameters

NameTypeInDescription
handlestringpathChannel login name (e.g. ninja)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/twitch/profiles/ninja" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Profile retrieved successfully.",
  "data": {
    "id": "19571641",
    "login": "ninja",
    "display_name": "Ninja",
    "description": "Just want to make people happy.",
    "avatar": "https://static-cdn.jtvnw.net/jtv_user_pictures/a.png",
    "followers": 19246072,
    "is_partner": true,
    "is_affiliate": false,
    "created_at": "2011-01-16T04:31:20Z",
    "is_live": false,
    "live_viewers": null,
    "last_broadcast": {
      "title": "Fortnite",
      "started_at": "2026-08-01T10:00:00Z"
    },
    "url": "https://twitch.tv/ninja"
  }
}
GET/twitch/profiles/{handle}/videos

Lists the videos a channel has published — title, duration in seconds, view count, thumbnail, and publish date.

Parameters

NameTypeInDescription
handlestringpathChannel login name
limitintegerquery·optVideos to return, 1–100. Default 20.
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/twitch/profiles/ninja/videos?limit=20" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Videos retrieved successfully.",
  "data": {
    "videos": [
      {
        "id": "999",
        "title": "Yesterday's stream",
        "duration_seconds": 7200,
        "views": 120000,
        "published_at": "2026-08-07T22:00:00Z",
        "thumbnail": "https://static-cdn.jtvnw.net/t.jpg",
        "url": "https://twitch.tv/videos/999"
      }
    ],
    "count": 1
  }
}

Linktree

Resolve a Linktree page into the links it actually points at — the natural next hop after finding a linktr.ee URL in an Instagram bio.

GET/linktree/profiles/{handle}

Returns a Linktree profile with every link on the page — title, destination URL, and block type. Blocks with no destination, such as headers and plain text, are left out of the links array. Pairs directly with /instagram/profile/{username}/contact: that endpoint surfaces the linktr.ee URL sitting in a bio, this one opens it up.

Parameters

NameTypeInDescription
handlestringpathLinktree handle, the part after linktr.ee/ (e.g. linktree)
bashrequest
curl "https://api.scrapingisnotacrime.com/v1/linktree/profiles/linktree" \
  -H "X-Api-Key: hob_••••••"
200 OKresponse
{
  "message": "Profile retrieved successfully.",
  "data": {
    "username": "linktree",
    "title": "@linktree",
    "description": "The only link in bio trusted by 70M+",
    "avatar": "https://ugc.production.linktr.ee/a.jpeg",
    "is_verified": true,
    "url": "https://linktr.ee/linktree",
    "links": [
      {
        "id": "1",
        "title": "Homepage",
        "url": "https://linktr.ee/",
        "type": "CLASSIC"
      }
    ]
  }
}

X / Twitter

coming soon

Tweets, profiles, search, timelines, trends, and reply threads from public X accounts.

In development. These endpoints are not yet available. The paths shown below reflect the planned API contract — follow our blog to be notified when they ship.

We use privacy-respecting Google Analytics on our public pages only — never inside your dashboard — to understand traffic. It's off until you opt in, and you can change your mind anytime in the footer.