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.
https://api.scrapingisnotacrime.com/v1Authentication
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.
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/cristiano" \ -H "X-Api-Key: hob_your_api_key"
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:
{ "message": "Profile retrieved successfully.", "data": "{ ... }" }
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.
{ "message": "The user @notfound does not exist or could not be found." }
| Status | Example message | Meaning |
|---|---|---|
| 200 | — | Success |
| 400 | Invalid username format. | Parameter missing or malformed. The wording names the parameter at fault — Invalid shortcode format., and so on. |
| 401 | Invalid X-Api-Key. | API key missing or not recognized. Also returned when a JWT session or workspace is invalid. |
| 402 | plan.quota_exceeded | The workspace has used up its scraping quota for the current period. |
| 404 | The user @notfound does not exist or could not be found. | Profile or content not found on the source platform. |
| 429 | Instagram rate limit reached. Please try again later. | The source platform is rate limiting us. The message names whichever platform it was. |
| 502 | Instagram could not be reached after multiple attempts. | Source platform unreachable after retries. |
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.
/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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Instagram username without @ |
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/instagram" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/instagram/profile/{username}/contactReturns 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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Instagram username without @ |
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/cafedaesquina/contact" \ -H "X-Api-Key: hob_••••••"
{ "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" } ] } }
/instagram/profile/{username}/timeline/latestReturns 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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Instagram username without @ |
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/instagram/timeline/latest" \ -H "X-Api-Key: hob_••••••"
{ "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" } ] } }
/instagram/profile/{username}/timelineWalks 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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Instagram username without @ |
count | integer | query·opt | Posts per page, 1–50. Default 12. This is a request, not a guarantee — Instagram may return fewer. |
cursor | string | query·opt | Cursor from the previous response's next_cursor. Omit it to fetch the first page. |
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/nasa/timeline?count=12&cursor=3950671748375397992_528817151" \ -H "X-Api-Key: hob_••••••"
{ "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" } }
/instagram/profile/{username}/highlightsLists 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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Instagram username without @ |
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/nasa/highlights" \ -H "X-Api-Key: hob_••••••"
{ "message": "Highlights retrieved successfully.", "data": { "username": "nasa", "user_id": "528817151", "highlights": [ { "id": "highlight:18201653992314974", "title": "Artemis III", "cover": "https://..." } ] } }
/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
| Name | Type | In | Description |
|---|---|---|---|
highlightId | string | path | Highlight id from the highlights listing, including the highlight: prefix |
curl "https://api.scrapingisnotacrime.com/v1/instagram/highlights/highlight:18201653992314974" \ -H "X-Api-Key: hob_••••••"
{ "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" } ] } }
/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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Instagram username without @ |
mediaId | string | path | Numeric media ID (e.g. 3123456789012345678) |
curl "https://api.scrapingisnotacrime.com/v1/instagram/profile/instagram/media/3123456789012345678" \ -H "X-Api-Key: hob_••••••"
{ "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" } }
/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
| Name | Type | In | Description |
|---|---|---|---|
shortcode | string | path | Post shortcode (e.g. C8xQz1aP9Kv from instagram.com/p/C8xQz1aP9Kv/) |
/instagram/media/{shortcode}/downloadReturns 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
| Name | Type | In | Description |
|---|---|---|---|
shortcode | string | path | Post, reel, or carousel shortcode (e.g. DbtErSrlB2J) |
curl "https://api.scrapingisnotacrime.com/v1/instagram/media/DbtErSrlB2J/download" \ -H "X-Api-Key: hob_••••••"
{ "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" } ] } }
/instagram/media/{shortcode}/idConverts 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
| Name | Type | In | Description |
|---|---|---|---|
shortcode | string | path | Post shortcode (e.g. Dbn-XJhk0_-) |
curl "https://api.scrapingisnotacrime.com/v1/instagram/media/Dbn-XJhk0_-/id" \ -H "X-Api-Key: hob_••••••"
{ "message": "Converted successfully.", "data": { "shortcode": "Dbn-XJhk0_-", "media_id": "3956405067326902270" } }
/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
| Name | Type | In | Description |
|---|---|---|---|
mediaId | string | path | Numeric media id, with or without the _ownerId suffix |
curl "https://api.scrapingisnotacrime.com/v1/instagram/media/id/3956405067326902270" \ -H "X-Api-Key: hob_••••••"
{ "message": "Converted successfully.", "data": { "media_id": "3956405067326902270", "shortcode": "Dbn-XJhk0_-" } }
/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
| Name | Type | In | Description |
|---|---|---|---|
shortcode | string | path | Reel shortcode (e.g. DyKlMnOpQrS from instagram.com/reels/DyKlMnOpQrS/) |
curl "https://api.scrapingisnotacrime.com/v1/instagram/reels/DyKlMnOpQrS" \ -H "X-Api-Key: hob_••••••"
{ "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.
/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
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | TikTok username without @ |
curl "https://api.scrapingisnotacrime.com/v1/tiktok/profile/tiktok" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/tiktok/video/{videoId}Returns full details for a single public TikTok video — view/like/share/comment counts, duration, cover image, and audio information.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
videoId | string | path | TikTok numeric video ID |
YouTube
List a channel's public videos by handle.
/youtube/channel/{handle}/videosReturns 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
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | Channel handle without @ (e.g. youtube) |
curl "https://api.scrapingisnotacrime.com/v1/youtube/channel/youtube/videos" \ -H "X-Api-Key: hob_••••••"
{ "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.
/appstore/searchSearches public apps on the Apple App Store by name or keyword. Returns app metadata — developer, price, rating, genres, screenshots, and more.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
term | string | query·req | Search term (e.g. instagram) |
country | string | query·opt | 2-letter country code (ISO 3166-1 alpha-2). Default us. |
limit | integer | query·opt | Number of results, 1–200. Default 10. |
curl "https://api.scrapingisnotacrime.com/v1/appstore/search?term=instagram&country=us&limit=1" \ -H "X-Api-Key: hob_••••••"
{ "message": "App Store search results retrieved successfully.", "data": { "term": "instagram", "country": "us", "resultCount": 1, "apps": [ { "id": 389801252, "bundleId": "com.burbn.instagram", "name": "Instagram", "developer": "Instagram, Inc.", "url": "https://apps.apple.com/us/app/instagram/id389801252", "iconUrl": "https://...", "price": 0, "currency": "USD", "rating": 4.7, "ratingCount": 25000000, "version": "312.0", "genres": [ "Photo & Video", "Social Networking" ], "screenshots": [ "https://..." ] } ] } }
/appstore/reviewsReturns 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
| Name | Type | In | Description |
|---|---|---|---|
appId | string | query·req | Numeric App Store app ID (the id field from a search result) |
country | string | query·opt | 2-letter country code (ISO 3166-1 alpha-2). Default us. |
page | integer | query·opt | Review page, 1–10. Default 1. |
curl "https://api.scrapingisnotacrime.com/v1/appstore/reviews?appId=389801252&country=us&page=1" \ -H "X-Api-Key: hob_••••••"
{ "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.
/github/profiles/{handle}Returns the public profile of a GitHub user — name, bio, company, location, blog, public repository count, and follower totals.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | GitHub username (e.g. torvalds) |
curl "https://api.scrapingisnotacrime.com/v1/github/profiles/torvalds" \ -H "X-Api-Key: hob_••••••"
{ "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" } }
/github/profiles/{handle}/followersLists the accounts following a user, one page at a time.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | GitHub username |
limit | integer | query·opt | Results per page, 1–100. Default 30. |
page | integer | query·opt | Page number, 1-based. Default 1. |
curl "https://api.scrapingisnotacrime.com/v1/github/profiles/torvalds/followers?limit=30&page=1" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/github/profiles/{handle}/followingLists the accounts a user follows. Same page shape and same user object as the followers endpoint.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | GitHub username |
limit | integer | query·opt | Results per page, 1–100. Default 30. |
page | integer | query·opt | Page number, 1-based. Default 1. |
/github/profiles/{handle}/repositoriesLists a user's public repositories, ordered by star count descending.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | GitHub username |
limit | integer | query·opt | Results per page, 1–100. Default 30. |
page | integer | query·opt | Page number, 1-based. Default 1. |
curl "https://api.scrapingisnotacrime.com/v1/github/profiles/torvalds/repositories?limit=30" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/github/repositoriesSearches 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
| Name | Type | In | Description |
|---|---|---|---|
q | string | query·req | Search query in GitHub syntax (e.g. stars:>10000 language:php) |
limit | integer | query·opt | Results per page, 1–100. Default 30. |
page | integer | query·opt | Page number, 1-based. Default 1. |
curl "https://api.scrapingisnotacrime.com/v1/github/repositories?q=stars:%3E10000+language:php&limit=1" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/github/trending/repositoriesRepositories gaining traction right now. GitHub exposes no trending endpoint officially, so this is derived from search: repositories created inside the requested window, ordered by stars. Treat it as a good proxy for the trending page, not a mirror of it.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
since | string | query·opt | Window to look back over — daily, weekly, or monthly. Default daily. |
language | string | query·opt | Restrict to a single language (e.g. php). |
limit | integer | query·opt | Results, 1–100. Default 30. |
curl "https://api.scrapingisnotacrime.com/v1/github/trending/repositories?since=weekly&language=php&limit=1" \ -H "X-Api-Key: hob_••••••"
{ "message": "Trending repositories retrieved successfully.", "data": { "items": [ { "name": "example-cli", "full_name": "acme/example-cli", "description": "A tiny CLI that got popular this week", "stars": 2400, "forks": 118, "language": "PHP", "topics": [ "cli" ], "is_fork": false, "is_archived": false, "created_at": "2026-08-04T09:12:00Z", "updated_at": "2026-08-10T22:41:00Z", "url": "https://github.com/acme/example-cli" } ], "total": 1200, "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.
/hackernews/feeds/{feed}Returns one of the site's feeds as a page of stories.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
feed | string | path | One of top, new, best, ask, show, job |
limit | integer | query·opt | Stories per page, 1–50. Default 20. |
page | integer | query·opt | Page number, 0-based. Default 0. |
curl "https://api.scrapingisnotacrime.com/v1/hackernews/feeds/top?limit=20&page=0" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/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
| Name | Type | In | Description |
|---|---|---|---|
id | integer | path | Hacker News item id (e.g. 8863) |
curl "https://api.scrapingisnotacrime.com/v1/hackernews/items/8863" \ -H "X-Api-Key: hob_••••••"
{ "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": [] } ] } }
/hackernews/searchFull-text search across Hacker News stories. Returns the same story object the feeds return.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
q | string | query·req | Search term (e.g. postgres) |
limit | integer | query·opt | Results per page, 1–50. Default 20. |
page | integer | query·opt | Page number, 0-based. Default 0. |
curl "https://api.scrapingisnotacrime.com/v1/hackernews/search?q=postgres&limit=20&page=0" \ -H "X-Api-Key: hob_••••••"
{ "message": "Search results retrieved successfully.", "data": { "items": [ { "id": 39872110, "title": "Postgres is all you need", "author": "dbweekly", "points": 1204, "num_comments": 488, "url": "https://example.test/postgres-is-all-you-need", "text": null, "created_at": "2026-03-18T08:44:02.000Z", "hn_url": "https://news.ycombinator.com/item?id=39872110" } ], "total": 500, "page": 0, "has_more": true } }
/hackernews/users/{username}Returns a user's public profile — karma, about text, account creation date, and total submission count.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Hacker News username (e.g. pg) |
curl "https://api.scrapingisnotacrime.com/v1/hackernews/users/pg" \ -H "X-Api-Key: hob_••••••"
{ "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" } }
/hackernews/users/{username}/submissionsLists the stories a user submitted, newest first.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Hacker News username |
limit | integer | query·opt | Results per page, 1–50. Default 20. |
page | integer | query·opt | Page number, 0-based. Default 0. |
curl "https://api.scrapingisnotacrime.com/v1/hackernews/users/pg/submissions?limit=20&page=0" \ -H "X-Api-Key: hob_••••••"
{ "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 } }
/hackernews/users/{username}/commentsLists the comments a user posted, newest first. Same page envelope as the other listings — items, total, page, has_more.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
username | string | path | Hacker News username |
limit | integer | query·opt | Results per page, 1–50. Default 20. |
page | integer | query·opt | Page number, 0-based. Default 0. |
Bluesky
Public Bluesky profiles and posts, addressed by full handle.
/bluesky/profiles/{handle}Returns a public profile — DID, display name, description, avatar and banner, follower and post counts.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | Full handle, including the domain (e.g. bsky.app or user.bsky.social) |
curl "https://api.scrapingisnotacrime.com/v1/bluesky/profiles/bsky.app" \ -H "X-Api-Key: hob_••••••"
{ "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" } }
/bluesky/profiles/{handle}/postsLists 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
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | Full handle, including the domain |
limit | integer | query·opt | Posts per page, 1–100. Default 25. |
cursor | string | query·opt | Cursor from the previous response's next_cursor. Omit for the first page. |
curl "https://api.scrapingisnotacrime.com/v1/bluesky/profiles/bsky.app/posts?limit=25" \ -H "X-Api-Key: hob_••••••"
{ "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.
/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
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | Channel login name (e.g. ninja) |
curl "https://api.scrapingisnotacrime.com/v1/twitch/profiles/ninja" \ -H "X-Api-Key: hob_••••••"
{ "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" } }
/twitch/profiles/{handle}/videosLists the videos a channel has published — title, duration in seconds, view count, thumbnail, and publish date.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | Channel login name |
limit | integer | query·opt | Videos to return, 1–100. Default 20. |
curl "https://api.scrapingisnotacrime.com/v1/twitch/profiles/ninja/videos?limit=20" \ -H "X-Api-Key: hob_••••••"
{ "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.
/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
| Name | Type | In | Description |
|---|---|---|---|
handle | string | path | Linktree handle, the part after linktr.ee/ (e.g. linktree) |
curl "https://api.scrapingisnotacrime.com/v1/linktree/profiles/linktree" \ -H "X-Api-Key: hob_••••••"
{ "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 soonTweets, profiles, search, timelines, trends, and reply threads from public X accounts.