Skip to content
capzy
All sites
Data API

Reddit Data API

Subreddits, posts, comments and search from across Reddit.

19endpointsCloudflare · Rate Limit handled for you
reddit.comGET/reddit/domain…GET/reddit/search…GET/reddit/search…{ } JSON
Example (captured live)
capzy.ai
Example
JSON responseclick the page to trace a field
{
"subreddit": "Capzy",
"sort": "hot",
"results": [
{
"id": "1vomatx",
"fullname": "t3_1vomatx",
"scraped_at": "2026-08-18T09:36:55.069292+00:00",
"title": "How to Get Amazon Product Data as JSON With One API Call",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "The fastest way to get Amazon product data as JSON is to call an endpoint that has already done the scraping, rendering, and parsing, and just hands you fields. This guide walks the Capzy Data API's Amazon coverage end to end: search, product detail, reviews, offers, bestsellers, and sellers, with real request and response shapes you can copy. # Why scraping Amazon yourself is a grind Amazon is one of the most-scraped sites on the internet, and it behaves accordingly. Product pages come in dozens of markup variants that change without notice. Listings render differently by region, device, and test bucket. And traffic that looks automated gets challenged early and often; we covered that side of the problem in [Amazon captchas and scraping](https://capzy.ai/blog/amazon-captcha-scraping). None of that work is differentiating for your product. The data is the point: titles, prices, ratings, availability. The endpoint layer exists so you can start at the data. # Search: from query to product list `amazon/search` takes a query and a marketplace and returns ranked results: curl -X POST https://api.capzy.ai/v1/data/amazon/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "wireless earbuds", "domain": "com", "page": 1}, "freshness": "auto" }' Each result row carries the product's `asin`, its `position` in the results, the `title`, and the marketplace `domain`. The `asin` is the key that unlocks every other Amazon endpoint, so a common pattern is search first, then fan out. # Product detail: the whole page, parsed `amazon/product_detail` takes an ASIN and returns the fields you would otherwise dig out of the page: import requests resp = requests.post( "https://api.capzy.ai/v1/data/amazon/product_detail", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"asin": "B0CHWRXH8B", "domain": "com"}, "freshness": "fresh", }, ) print(resp.json()) A trimmed real response: { "asin": "B0CHWRXH8B", "title": "Apple AirPods Pro (2nd Generation) Wireless Ear Buds...", "brand": "Apple Store", "price": 163.19, "currency": "USD", "rating": 4.7, "reviews_count": 28856, "availability": "Only 1 left in stock - order soon." } Note the `freshness: "fresh"` there: for a price or availability check you usually want a live pull. For enrichment jobs where an hour-old record is fine, `auto` avoids hammering the same page. # The rest of the Amazon family The same request shape covers the whole surface: * `amazon/reviews`: paginated reviews for an ASIN * `amazon/offers`: the buy-box and competing offers for a listing * `amazon/bestsellers` and `amazon/new_releases`: category charts * `amazon/deals`: current deal listings * `amazon/seller`**,** `amazon/seller_products`**,** `amazon/seller_feedback`: storefront data for marketplace sellers * `amazon/category` **and** `amazon/categories`: category browse trees * `amazon/autocomplete`: search suggestions for a prefix If you have ever built keyword tooling, the autocomplete endpoint alone is a feature: it is the same suggestion stream shoppers see as they type. # Going international Every Amazon endpoint takes a `domain` parameter that selects the marketplace: `com` for the US, [`co.uk`](http://co.uk), `de`, `fr`, [`co.jp`](http://co.jp), [`com.au`](http://com.au), and the rest of the 20 supported marketplaces. The response schema stays identical across all of them, so a price-comparison feature across five countries is the same code running five parameter sets, not five scrapers. # Keeping a catalog fresh without re-pulling everything Real Amazon workloads are rarely one call. Three features carry the load at volume: * **Batch requests**: send many ASINs against `product_detail` in one call, and each query is tracked as its own job * **Freshness modes**: `cached` and `auto` let enrichment reads reuse recent records instead of hitting the live site for every caller * **Saved tasks and schedules**: store "bestsellers in this category, every morning" once, then collect the runs from your request history or export them as CSV, JSON, or XLSX # A tiny end-to-end example Search for a product, take the top result, and pull its full detail: import requests BASE = "https://api.capzy.ai/v1/data" KEY = "YOUR_DATA_API_KEY" search = requests.post(f"{BASE}/amazon/search", json={ "clientKey": KEY, "params": {"query": "mechanical keyboard", "domain": "com", "page": 1}, "freshness": "auto", }).json() top = search["results"][0] detail = requests.post(f"{BASE}/amazon/product_detail", json={ "clientKey": KEY, "params": {"asin": top["asin"], "domain": "com"}, "freshness": "fresh", }).json() print(top["asin"], detail["title"], detail.get("price")) That is the entire integration. No headless browser, no HTML, no selectors, and nothing to fix when the page changes next month. # Wrap-up Amazon data as JSON is a solved problem when the parsing lives behind an endpoint. Start with `amazon/search` for discovery, `amazon/product_detail` for depth, and layer reviews, offers, and seller endpoints as your feature needs them. The full parameter and schema reference for every endpoint lives in the Data API docs.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>The fastest way to get Amazon product data as JSON is to call an endpoint that has already done the scraping, rendering, and parsing, and just hands you fields. This guide walks the Capzy Data API's Amazon coverage end to end: search, product detail, reviews, offers, bestsellers, and sellers, with real request and response shapes you can copy.</p> <h1>Why scraping Amazon yourself is a grind</h1> <p>Amazon is one of the most-scraped sites on the internet, and it behaves accordingly. Product pages come in dozens of markup variants that change without notice. Listings render differently by region, device, and test bucket. And traffic that looks automated gets challenged early and often; we covered that side of the problem in <a href="https://capzy.ai/blog/amazon-captcha-scraping">Amazon captchas and scraping</a>.</p> <p>None of that work is differentiating for your product. The data is the point: titles, prices, ratings, availability. The endpoint layer exists so you can start at the data.</p> <h1>Search: from query to product list</h1> <p><code>amazon/search</code> takes a query and a marketplace and returns ranked results:</p> <pre><code>curl -X POST https://api.capzy.ai/v1/data/amazon/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "wireless earbuds", "domain": "com", "page": 1}, "freshness": "auto" }' </code></pre> <p>Each result row carries the product's <code>asin</code>, its <code>position</code> in the results, the <code>title</code>, and the marketplace <code>domain</code>. The <code>asin</code> is the key that unlocks every other Amazon endpoint, so a common pattern is search first, then fan out.</p> <h1>Product detail: the whole page, parsed</h1> <p><code>amazon/product_detail</code> takes an ASIN and returns the fields you would otherwise dig out of the page:</p> <pre><code>import requests resp = requests.post( "https://api.capzy.ai/v1/data/amazon/product_detail", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"asin": "B0CHWRXH8B", "domain": "com"}, "freshness": "fresh", }, ) print(resp.json()) </code></pre> <p>A trimmed real response:</p> <pre><code>{ "asin": "B0CHWRXH8B", "title": "Apple AirPods Pro (2nd Generation) Wireless Ear Buds...", "brand": "Apple Store", "price": 163.19, "currency": "USD", "rating": 4.7, "reviews_count": 28856, "availability": "Only 1 left in stock - order soon." } </code></pre> <p>Note the <code>freshness: "fresh"</code> there: for a price or availability check you usually want a live pull. For enrichment jobs where an hour-old record is fine, <code>auto</code> avoids hammering the same page.</p> <h1>The rest of the Amazon family</h1> <p>The same request shape covers the whole surface:</p> <ul> <li><code>amazon/reviews</code>: paginated reviews for an ASIN</li> <li><code>amazon/offers</code>: the buy-box and competing offers for a listing</li> <li><code>amazon/bestsellers</code> and <code>amazon/new_releases</code>: category charts</li> <li><code>amazon/deals</code>: current deal listings</li> <li><code>amazon/seller</code><strong>,</strong> <code>amazon/seller_products</code><strong>,</strong> <code>amazon/seller_feedback</code>: storefront data for marketplace sellers</li> <li><code>amazon/category</code> <strong>and</strong> <code>amazon/categories</code>: category browse trees</li> <li><code>amazon/autocomplete</code>: search suggestions for a prefix</li> </ul> <p>If you have ever built keyword tooling, the autocomplete endpoint alone is a feature: it is the same suggestion stream shoppers see as they type.</p> <h1>Going international</h1> <p>Every Amazon endpoint takes a <code>domain</code> parameter that selects the marketplace: <code>com</code> for the US, <a href="http://co.uk"><code>co.uk</code></a>, <code>de</code>, <code>fr</code>, <a href="http://co.jp"><code>co.jp</code></a>, <a href="http://com.au"><code>com.au</code></a>, and the rest of the 20 supported marketplaces. The response schema stays identical across all of them, so a price-comparison feature across five countries is the same code running five parameter sets, not five scrapers.</p> <h1>Keeping a catalog fresh without re-pulling everything</h1> <p>Real Amazon workloads are rarely one call. Three features carry the load at volume:</p> <ul> <li><strong>Batch requests</strong>: send many ASINs against <code>product_detail</code> in one call, and each query is tracked as its own job</li> <li><strong>Freshness modes</strong>: <code>cached</code> and <code>auto</code> let enrichment reads reuse recent records instead of hitting the live site for every caller</li> <li><strong>Saved tasks and schedules</strong>: store "bestsellers in this category, every morning" once, then collect the runs from your request history or export them as CSV, JSON, or XLSX</li> </ul> <h1>A tiny end-to-end example</h1> <p>Search for a product, take the top result, and pull its full detail:</p> <pre><code>import requests BASE = "https://api.capzy.ai/v1/data" KEY = "YOUR_DATA_API_KEY" search = requests.post(f"{BASE}/amazon/search", json={ "clientKey": KEY, "params": {"query": "mechanical keyboard", "domain": "com", "page": 1}, "freshness": "auto", }).json() top = search["results"][0] detail = requests.post(f"{BASE}/amazon/product_detail", json={ "clientKey": KEY, "params": {"asin": top["asin"], "domain": "com"}, "freshness": "fresh", }).json() print(top["asin"], detail["title"], detail.get("price")) </code></pre> <p>That is the entire integration. No headless browser, no HTML, no selectors, and nothing to fix when the page changes next month.</p> <h1>Wrap-up</h1> <p>Amazon data as JSON is a solved problem when the parsing lives behind an endpoint. Start with <code>amazon/search</code> for discovery, <code>amazon/product_detail</code> for depth, and layer reviews, offers, and seller endpoints as your feature needs them. The full parameter and schema reference for every endpoint lives in the Data API docs.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/get-amazon-product-data-json-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vomatx/how_to_get_amazon_product_data_as_json_with_one/",
"url_overridden_by_dest": "https://capzy.ai/blog/get-amazon-product-data-json-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T23:00:29+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/eljwbR9g2_N58Qgr8Q1Ax_o5vwJ7sODbNTC0LacG1xI.jpeg?width=140&height=73&auto=webp&s=75ef584da2fbda7a4371d79a0363c202de8cef53",
"preview_image": "https://external-preview.redd.it/eljwbR9g2_N58Qgr8Q1Ax_o5vwJ7sODbNTC0LacG1xI.jpeg?auto=webp&s=11915280273dc626033d1591a8b349b45a1c1277",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vomado",
"fullname": "t3_1vomado",
"scraped_at": "2026-08-18T09:36:55.069326+00:00",
"title": "How to Get TikTok Data as JSON: Videos, Users, and Trends",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "TikTok is one of the harder sites to pull data from cleanly, which is exactly why an endpoint that hands you JSON is worth it. The Capzy Data API covers TikTok search, videos, users, and trends, each a single request that returns structured fields in a few seconds. TikTok signs its requests and guards its endpoints heavily, so a do-it-yourself scraper is a lot of moving parts to maintain. All of that is behind the API. You ask for videos, you get videos. # Search curl -X POST https://api.capzy.ai/v1/data/tiktok/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "funny cats", "region": "us"}, "freshness": "auto" }' There are focused variants too, `tiktok/search_videos`, `search_users`, and `search_hashtags`, when you want just one kind of result. # Video and user data import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" video = requests.post(f"{BASE}/tiktok/video", json={ "clientKey": KEY, "params": {"video_id": "6718335390845095173", "region": "us"}, "freshness": "auto", }).json() user_clips = requests.post(f"{BASE}/tiktok/user_videos", json={ "clientKey": KEY, "params": {"username": "tiktok", "region": "us"}, "freshness": "auto", }).json() `tiktok/user` returns a creator's profile, and `user_videos` walks their posts. There is even a `video_transcript` endpoint for the spoken audio. # Trends Content teams lean on the trend endpoints: `tiktok/trending_videos`, `trending_hashtags`, and `trending_songs`, all by region. A weekly scheduled pull of those three is a research dashboard on its own. # A tiny end-to-end example Find creators for a topic, then pull the first one's recent videos: import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" users = requests.post(f"{BASE}/tiktok/search_users", json={ "clientKey": KEY, "params": {"query": "home cooking", "region": "us"}, "freshness": "auto", }).json() handle = users["results"][0]["username"] clips = requests.post(f"{BASE}/tiktok/user_videos", json={ "clientKey": KEY, "params": {"username": handle, "region": "us"}, "freshness": "auto", }).json() print(handle, len(clips.get("results", [])), "videos") No signing, no browser fleet, no parsing. For the bigger picture, see [what a Data API is](https://capzy.ai/blog/what-is-a-data-api) and [seven pipelines you can build](https://capzy.ai/blog/data-api-use-cases).",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>TikTok is one of the harder sites to pull data from cleanly, which is exactly why an endpoint that hands you JSON is worth it. The Capzy Data API covers TikTok search, videos, users, and trends, each a single request that returns structured fields in a few seconds.</p> <p>TikTok signs its requests and guards its endpoints heavily, so a do-it-yourself scraper is a lot of moving parts to maintain. All of that is behind the API. You ask for videos, you get videos.</p> <h1>Search</h1> <pre><code>curl -X POST https://api.capzy.ai/v1/data/tiktok/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "funny cats", "region": "us"}, "freshness": "auto" }' </code></pre> <p>There are focused variants too, <code>tiktok/search_videos</code>, <code>search_users</code>, and <code>search_hashtags</code>, when you want just one kind of result.</p> <h1>Video and user data</h1> <pre><code>import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" video = requests.post(f"{BASE}/tiktok/video", json={ "clientKey": KEY, "params": {"video_id": "6718335390845095173", "region": "us"}, "freshness": "auto", }).json() user_clips = requests.post(f"{BASE}/tiktok/user_videos", json={ "clientKey": KEY, "params": {"username": "tiktok", "region": "us"}, "freshness": "auto", }).json() </code></pre> <p><code>tiktok/user</code> returns a creator's profile, and <code>user_videos</code> walks their posts. There is even a <code>video_transcript</code> endpoint for the spoken audio.</p> <h1>Trends</h1> <p>Content teams lean on the trend endpoints: <code>tiktok/trending_videos</code>, <code>trending_hashtags</code>, and <code>trending_songs</code>, all by region. A weekly scheduled pull of those three is a research dashboard on its own.</p> <h1>A tiny end-to-end example</h1> <p>Find creators for a topic, then pull the first one's recent videos:</p> <pre><code>import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" users = requests.post(f"{BASE}/tiktok/search_users", json={ "clientKey": KEY, "params": {"query": "home cooking", "region": "us"}, "freshness": "auto", }).json() handle = users["results"][0]["username"] clips = requests.post(f"{BASE}/tiktok/user_videos", json={ "clientKey": KEY, "params": {"username": handle, "region": "us"}, "freshness": "auto", }).json() print(handle, len(clips.get("results", [])), "videos") </code></pre> <p>No signing, no browser fleet, no parsing. For the bigger picture, see <a href="https://capzy.ai/blog/what-is-a-data-api">what a Data API is</a> and <a href="https://capzy.ai/blog/data-api-use-cases">seven pipelines you can build</a>.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/tiktok-data-json-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vomado/how_to_get_tiktok_data_as_json_videos_users_and/",
"url_overridden_by_dest": "https://capzy.ai/blog/tiktok-data-json-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T23:00:02+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/AaPSdnvnmOWd4YU0qbNu72qWM61H_-JM6EL0qz999GY.jpeg?width=140&height=73&auto=webp&s=7a92cfc87fb17f16606e8bc2090366131092c5ab",
"preview_image": "https://external-preview.redd.it/AaPSdnvnmOWd4YU0qbNu72qWM61H_-JM6EL0qz999GY.jpeg?auto=webp&s=ae879d071da3266d9768d5f56e7fe216028b569a",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1voma2f",
"fullname": "t3_1voma2f",
"scraped_at": "2026-08-18T09:36:55.069336+00:00",
"title": "How to Get Instagram Data as JSON: Profiles, Posts, and Hashtags",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "The Capzy Data API turns public Instagram data into clean JSON: a profile, a user's posts, a single media item, or a hashtag feed. Each is one request and a few seconds, with the access and parsing handled for you. Instagram is notoriously tricky to pull from directly. That difficulty is the whole reason to use an endpoint, and it is exactly what sits behind these calls. # Profile and posts curl -X POST https://api.capzy.ai/v1/data/instagram/user \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"username": "instagram"}, "freshness": "auto" }' `instagram/user` returns the public profile: follower count, bio, and post count. To walk the grid, pass the handle to `user_posts`: import requests posts = requests.post("https://api.capzy.ai/v1/data/instagram/user_posts", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"username": "instagram", "amount": 12}, "freshness": "auto", }).json() `amount` caps how many posts you pull per call, so you can keep runs small and cheap. # Media and hashtags A single post has its own endpoint, `instagram/media`, taking the shortcode from a post URL. For discovery, `instagram/hashtag` and `hashtag_recent` return posts for a tag, which is the basis of a lot of campaign and trend tracking. # A tiny end-to-end example Pull a profile, then its recent posts: import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" profile = requests.post(f"{BASE}/instagram/user", json={ "clientKey": KEY, "params": {"username": "natgeo"}, "freshness": "auto", }).json() grid = requests.post(f"{BASE}/instagram/user_posts", json={ "clientKey": KEY, "params": {"username": "natgeo", "amount": 12}, "freshness": "auto", }).json() print(profile.get("username"), "-", len(grid.get("results", [])), "recent posts") That is the whole thing: no login flow, no browser, no HTML. For where a social feed fits in a larger pipeline, see [seven pipelines you can build](https://capzy.ai/blog/data-api-use-cases), or [start with the basics](https://capzy.ai/blog/what-is-a-data-api).",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>The Capzy Data API turns public Instagram data into clean JSON: a profile, a user's posts, a single media item, or a hashtag feed. Each is one request and a few seconds, with the access and parsing handled for you.</p> <p>Instagram is notoriously tricky to pull from directly. That difficulty is the whole reason to use an endpoint, and it is exactly what sits behind these calls.</p> <h1>Profile and posts</h1> <pre><code>curl -X POST https://api.capzy.ai/v1/data/instagram/user \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"username": "instagram"}, "freshness": "auto" }' </code></pre> <p><code>instagram/user</code> returns the public profile: follower count, bio, and post count. To walk the grid, pass the handle to <code>user_posts</code>:</p> <pre><code>import requests posts = requests.post("https://api.capzy.ai/v1/data/instagram/user_posts", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"username": "instagram", "amount": 12}, "freshness": "auto", }).json() </code></pre> <p><code>amount</code> caps how many posts you pull per call, so you can keep runs small and cheap.</p> <h1>Media and hashtags</h1> <p>A single post has its own endpoint, <code>instagram/media</code>, taking the shortcode from a post URL. For discovery, <code>instagram/hashtag</code> and <code>hashtag_recent</code> return posts for a tag, which is the basis of a lot of campaign and trend tracking.</p> <h1>A tiny end-to-end example</h1> <p>Pull a profile, then its recent posts:</p> <pre><code>import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" profile = requests.post(f"{BASE}/instagram/user", json={ "clientKey": KEY, "params": {"username": "natgeo"}, "freshness": "auto", }).json() grid = requests.post(f"{BASE}/instagram/user_posts", json={ "clientKey": KEY, "params": {"username": "natgeo", "amount": 12}, "freshness": "auto", }).json() print(profile.get("username"), "-", len(grid.get("results", [])), "recent posts") </code></pre> <p>That is the whole thing: no login flow, no browser, no HTML. For where a social feed fits in a larger pipeline, see <a href="https://capzy.ai/blog/data-api-use-cases">seven pipelines you can build</a>, or <a href="https://capzy.ai/blog/what-is-a-data-api">start with the basics</a>.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/instagram-data-json-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1voma2f/how_to_get_instagram_data_as_json_profiles_posts/",
"url_overridden_by_dest": "https://capzy.ai/blog/instagram-data-json-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T22:59:39+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/stziKUFo-0t0xOJ0WyvqGKmmoCAz9Z-VFk6Nyt9EFZ4.jpeg?width=140&height=73&auto=webp&s=de71cf80d883fdd1b9bd3da07e86cacaf53c522e",
"preview_image": "https://external-preview.redd.it/stziKUFo-0t0xOJ0WyvqGKmmoCAz9Z-VFk6Nyt9EFZ4.jpeg?auto=webp&s=b765ab057a3029d8c8935d47b7874e500f07bb37",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vom9me",
"fullname": "t3_1vom9me",
"scraped_at": "2026-08-18T09:36:55.069344+00:00",
"title": "How to Get Zillow Real Estate Data as JSON",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "Real estate teams want listings, price changes, and property details as data, not a scraping project. The Capzy Data API turns Zillow into a handful of JSON endpoints: search a market, pull a property, look up an agent. Each is one request and a few seconds. Real estate portals are aggressive about automated access and change their markup often, which is why a hand-rolled Zillow scraper is a treadmill. That whole problem is on our side of the endpoint. # Search a market curl -X POST https://api.capzy.ai/v1/data/zillow/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"location": "Austin, TX", "status": "for_sale", "page": 1}, "freshness": "fresh" }' `status` selects `for_sale`, `for_rent`, or `sold`, and results page with `page`. Each row carries a `zpid`, Zillow's property ID, which unlocks the detail endpoint. For live inventory you usually want `freshness: "fresh"`. # Property detail import requests detail = requests.post("https://api.capzy.ai/v1/data/zillow/property", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"zpid": "2101031832"}, "freshness": "auto", }).json() print(detail.get("price"), detail.get("address")) This returns the structured property record: price, beds and baths, square footage, history, and the rest, without you touching a single line of HTML. # Agents and autocomplete Two more endpoints round out most workflows. `zillow/agent` takes an agent `id` and returns their profile and listings, and `zillow/autocomplete` turns a partial "Austin, T" into resolvable locations, which is handy for building a clean search UI. # A tiny end-to-end example Find for-sale listings in a market, then enrich the first one: import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" results = requests.post(f"{BASE}/zillow/search", json={ "clientKey": KEY, "params": {"location": "Austin, TX", "status": "for_sale", "page": 1}, "freshness": "fresh", }).json() zpid = results["results"][0]["zpid"] prop = requests.post(f"{BASE}/zillow/property", json={ "clientKey": KEY, "params": {"zpid": zpid}, "freshness": "auto", }).json() print(zpid, prop.get("price")) Schedule that search as a saved task and you have a daily market feed you can export to CSV or XLSX. The same catalog covers other portals too, so one pipeline shape can serve several markets. New here? [What a Data API is](https://capzy.ai/blog/what-is-a-data-api).",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>Real estate teams want listings, price changes, and property details as data, not a scraping project. The Capzy Data API turns Zillow into a handful of JSON endpoints: search a market, pull a property, look up an agent. Each is one request and a few seconds.</p> <p>Real estate portals are aggressive about automated access and change their markup often, which is why a hand-rolled Zillow scraper is a treadmill. That whole problem is on our side of the endpoint.</p> <h1>Search a market</h1> <pre><code>curl -X POST https://api.capzy.ai/v1/data/zillow/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"location": "Austin, TX", "status": "for_sale", "page": 1}, "freshness": "fresh" }' </code></pre> <p><code>status</code> selects <code>for_sale</code>, <code>for_rent</code>, or <code>sold</code>, and results page with <code>page</code>. Each row carries a <code>zpid</code>, Zillow's property ID, which unlocks the detail endpoint. For live inventory you usually want <code>freshness: "fresh"</code>.</p> <h1>Property detail</h1> <pre><code>import requests detail = requests.post("https://api.capzy.ai/v1/data/zillow/property", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"zpid": "2101031832"}, "freshness": "auto", }).json() print(detail.get("price"), detail.get("address")) </code></pre> <p>This returns the structured property record: price, beds and baths, square footage, history, and the rest, without you touching a single line of HTML.</p> <h1>Agents and autocomplete</h1> <p>Two more endpoints round out most workflows. <code>zillow/agent</code> takes an agent <code>id</code> and returns their profile and listings, and <code>zillow/autocomplete</code> turns a partial "Austin, T" into resolvable locations, which is handy for building a clean search UI.</p> <h1>A tiny end-to-end example</h1> <p>Find for-sale listings in a market, then enrich the first one:</p> <pre><code>import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" results = requests.post(f"{BASE}/zillow/search", json={ "clientKey": KEY, "params": {"location": "Austin, TX", "status": "for_sale", "page": 1}, "freshness": "fresh", }).json() zpid = results["results"][0]["zpid"] prop = requests.post(f"{BASE}/zillow/property", json={ "clientKey": KEY, "params": {"zpid": zpid}, "freshness": "auto", }).json() print(zpid, prop.get("price")) </code></pre> <p>Schedule that search as a saved task and you have a daily market feed you can export to CSV or XLSX. The same catalog covers other portals too, so one pipeline shape can serve several markets. New here? <a href="https://capzy.ai/blog/what-is-a-data-api">What a Data API is</a>.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/zillow-real-estate-data-json-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vom9me/how_to_get_zillow_real_estate_data_as_json/",
"url_overridden_by_dest": "https://capzy.ai/blog/zillow-real-estate-data-json-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T22:59:04+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/pN6VPhFjForfpxRZiXfXn9PDCVmO5aGN77-x8hxGnR0.jpeg?width=140&height=73&auto=webp&s=6545d782ce08d6df072d71b24365d652a6c7996d",
"preview_image": "https://external-preview.redd.it/pN6VPhFjForfpxRZiXfXn9PDCVmO5aGN77-x8hxGnR0.jpeg?auto=webp&s=0fd49fcb64f7229fef6525f9bee220e6777c0a49",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vom9en",
"fullname": "t3_1vom9en",
"scraped_at": "2026-08-18T09:36:55.069353+00:00",
"title": "How to Get Google Search Results as JSON With One API Call",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "The quickest way to get Google search results as JSON is to call an endpoint that already did the search, cleared the challenges, and parsed the page for you. You send a query, you get back ranked results with positions, titles, and links, usually in a few seconds. No browser, no HTML, no selectors that break next week. Google is one of the hardest surfaces to scrape by hand: the markup shifts constantly, results change by region and device, and automated traffic gets challenged fast. All of that lives behind the endpoint here. You start at the data. # Web search One request shape covers everything. Send the platform and action in the URL, the parameters in `params`: curl -X POST https://api.capzy.ai/v1/data/google/web_search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "best coffee makers", "gl": "us", "hl": "en", "page": 1}, "freshness": "fresh" }' `gl` is the country and `hl` is the interface language, so the same query is trivially repeatable across markets. Each result row carries its `position`, which is all you need to turn a daily run into a rank chart. We went deeper on that pattern in [scraping search results (SERP)](https://capzy.ai/blog/scraping-search-results-serp). # News and maps The Google catalog is broad, and the sibling verticals use the same call: import requests BASE = "https://api.capzy.ai/v1/data" news = requests.post(f"{BASE}/google/news_search", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "electric vehicles", "gl": "us"}, "freshness": "auto", }).json() places = requests.post(f"{BASE}/google/maps_search", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "coffee near austin", "gl": "us"}, "freshness": "auto", }).json() Beyond these there are images, shopping, scholar, jobs, finance, trends, and more, each documented with its own parameters and response schema. If you have used one Google endpoint, you have used them all. # Freshness, so you are not over-fetching Every call takes a `freshness` mode. Use `fresh` when you need this second's ranking, `auto` to reuse a recent result when one is new enough, and `cached` to serve a stored record outright. Identical requests fired at the same moment are collapsed into one, so a burst of the same query does not multiply anything on your side. # A tiny end-to-end example Rank-track one keyword across two countries in a few lines: import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" for gl in ("us", "gb"): r = requests.post(f"{BASE}/google/web_search", json={ "clientKey": KEY, "params": {"query": "noise cancelling headphones", "gl": gl, "hl": "en"}, "freshness": "fresh", }).json() top = r["results"][0] print(gl, top["position"], top["title"]) That is the whole integration. The blocking, the rendering, and the parsing are our job. Getting automation past modern bot defenses is Capzy's core business, and every Google endpoint rides on that same engine. New to the idea? Start with [what a Data API is](https://capzy.ai/blog/what-is-a-data-api).",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>The quickest way to get Google search results as JSON is to call an endpoint that already did the search, cleared the challenges, and parsed the page for you. You send a query, you get back ranked results with positions, titles, and links, usually in a few seconds. No browser, no HTML, no selectors that break next week.</p> <p>Google is one of the hardest surfaces to scrape by hand: the markup shifts constantly, results change by region and device, and automated traffic gets challenged fast. All of that lives behind the endpoint here. You start at the data.</p> <h1>Web search</h1> <p>One request shape covers everything. Send the platform and action in the URL, the parameters in <code>params</code>:</p> <pre><code>curl -X POST https://api.capzy.ai/v1/data/google/web_search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "best coffee makers", "gl": "us", "hl": "en", "page": 1}, "freshness": "fresh" }' </code></pre> <p><code>gl</code> is the country and <code>hl</code> is the interface language, so the same query is trivially repeatable across markets. Each result row carries its <code>position</code>, which is all you need to turn a daily run into a rank chart. We went deeper on that pattern in <a href="https://capzy.ai/blog/scraping-search-results-serp">scraping search results (SERP)</a>.</p> <h1>News and maps</h1> <p>The Google catalog is broad, and the sibling verticals use the same call:</p> <pre><code>import requests BASE = "https://api.capzy.ai/v1/data" news = requests.post(f"{BASE}/google/news_search", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "electric vehicles", "gl": "us"}, "freshness": "auto", }).json() places = requests.post(f"{BASE}/google/maps_search", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "coffee near austin", "gl": "us"}, "freshness": "auto", }).json() </code></pre> <p>Beyond these there are images, shopping, scholar, jobs, finance, trends, and more, each documented with its own parameters and response schema. If you have used one Google endpoint, you have used them all.</p> <h1>Freshness, so you are not over-fetching</h1> <p>Every call takes a <code>freshness</code> mode. Use <code>fresh</code> when you need this second's ranking, <code>auto</code> to reuse a recent result when one is new enough, and <code>cached</code> to serve a stored record outright. Identical requests fired at the same moment are collapsed into one, so a burst of the same query does not multiply anything on your side.</p> <h1>A tiny end-to-end example</h1> <p>Rank-track one keyword across two countries in a few lines:</p> <pre><code>import requests BASE, KEY = "https://api.capzy.ai/v1/data", "YOUR_DATA_API_KEY" for gl in ("us", "gb"): r = requests.post(f"{BASE}/google/web_search", json={ "clientKey": KEY, "params": {"query": "noise cancelling headphones", "gl": gl, "hl": "en"}, "freshness": "fresh", }).json() top = r["results"][0] print(gl, top["position"], top["title"]) </code></pre> <p>That is the whole integration. The blocking, the rendering, and the parsing are our job. Getting automation past modern bot defenses is Capzy's core business, and every Google endpoint rides on that same engine. New to the idea? Start with <a href="https://capzy.ai/blog/what-is-a-data-api">what a Data API is</a>.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/google-search-data-json-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vom9en/how_to_get_google_search_results_as_json_with_one/",
"url_overridden_by_dest": "https://capzy.ai/blog/google-search-data-json-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T22:58:49+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/G5KVv29PzGGBeG_njA5aCWcahh2H0mxDIpbyxaZe3wM.jpeg?width=140&height=73&auto=webp&s=10fb45684704b3568524b50eb92e00d08d7da86e",
"preview_image": "https://external-preview.redd.it/G5KVv29PzGGBeG_njA5aCWcahh2H0mxDIpbyxaZe3wM.jpeg?auto=webp&s=cc56135b637575867bd7dfecfe5f6ca03548df26",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vol52h",
"fullname": "t3_1vol52h",
"scraped_at": "2026-08-18T09:36:55.069361+00:00",
"title": "What Is a Data API? Structured JSON From the Web's Biggest Sites",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "A Data API is a REST API that returns structured data from websites through documented endpoints. Instead of fetching HTML and parsing it yourself, you call something like `amazon/search` or `google/search` with a few parameters, and you get back JSON with named fields: titles, prices, ratings, positions, links. Everything between your request and that JSON, including fetching, rendering, getting past anti-bot checks, and parsing, happens on the other side of the endpoint. That is the whole idea, and it is worth being precise about because "we have data" can mean a lot of things. A Data API is not a dataset dump, not a browser you drive, and not a generic proxy layer. It is a catalog of per-site, per-action endpoints with stable schemas you can build product features on. # The problem it replaces If you have ever maintained your own scraper for a major site, you know the lifecycle. Week one, it works. Week three, a markup change silently breaks your price field. Week six, the site starts challenging your traffic and you are suddenly in the browser-automation and fingerprinting business instead of whatever business you meant to be in. We wrote about that grind in [why you get blocked](https://capzy.ai/blog/why-you-get-blocked-bot-signals) and [building a resilient scraper](https://capzy.ai/blog/build-a-resilient-scraper). The sites with the most valuable data are also the hardest ones to scrape. That is not a coincidence: demand attracts defenses. So teams end up spending their scraping effort on the exact targets where the maintenance burden is highest. A Data API moves that burden across the API boundary. When a site changes its markup or rotates its defenses, the fix ships on our side and your integration does not change. Your code keeps calling the same endpoint and parsing the same fields. # How the Capzy Data API works Every endpoint follows one request shape: curl -X POST https://api.capzy.ai/v1/data/amazon/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "wireless earbuds", "domain": "com", "page": 1}, "freshness": "auto" }' The platform and action live in the URL, the endpoint's parameters go in `params`, and the response is parsed JSON: { "errorId": 0, "status": "ready", "results": [ { "position": 1, "asin": "B0H5W39V6T", "title": "Wireless Earbuds, Sports Bluetooth Headphones...", "domain": "com" } ] } Learn one endpoint and you have learned them all: `zillow/search`, `youtube/search`, `reddit/posts`, and the rest of the catalog behave the same way, with the same envelope and the same conventions. Long-running pulls return a `jobId` you can poll on `GET /v1/data/jobs/{job_id}`, so nothing forces you to hold a connection open. # What you can pull from it The catalog covers 27 platforms with more than 300 documented endpoints, grouped roughly like this: * **Commerce:** Amazon (search, product detail, offers, reviews, bestsellers, deals, sellers, across 20 marketplaces), eBay, Walmart, Vinted, Shopee, Depop * **Search:** Google across its verticals (web, images, news, maps, shopping, scholar, trends, jobs, finance), plus Bing, DuckDuckGo, and Baidu * **Social and video:** YouTube, Twitter/X, TikTok, Reddit, Instagram, Facebook, LinkedIn public pages * **Real estate:** Zillow, Realtor, LoopNet, Immobiliare, Leboncoin, [Apartments.com](http://Apartments.com) * **AI answers:** ask endpoints that capture what AI assistants say, useful for brand monitoring Each endpoint documents its parameters and its response schema, so you know the field names before you make the first call, and they stay put afterward. # Freshness: cached, auto, or live Every call takes a `freshness` mode, and it is one of the most practically useful parts of the API: * `fresh` forces a live pull from the site right now * `cached` serves a recent stored record when one exists * `auto` uses a recent record when it is new enough for that endpoint and pulls live otherwise Identical requests that are in flight at the same moment are coalesced into a single pull. If your dashboard fires the same product lookup five times in one minute, the site sees one visit and your five callers all get the result. You choose per request how fresh the data needs to be, which is a much better dial than "re-scrape everything, always". # Beyond single calls The API is the core, but most real pipelines need a little machinery around it, so that machinery is built in: * **Batch requests** submit many queries against one endpoint in a single call, each tracked as its own job * **Saved tasks** store a configured request (platform, action, params, freshness) so you can re-run it in one click or on a schedule * **Request history** keeps every run with its status and a result snapshot you can review later * **Exports** turn any run's records into CSV, JSON, or XLSX from the console, no export script required If your workflow is "pull this every morning and hand the spreadsheet to the team", that is a saved task, a schedule, and an export button, not a service you have to write. # Where it fits in your stack A Data API is the right tool when the sites you need are major platforms and what you want is their data, not a scraping project. If your target is a long-tail site the catalog does not cover, or you need custom crawl logic, a general-purpose scraping approach still makes sense, and the two compose well: catalog endpoints for the head of your workload, custom scraping for the tail. For the architectural view of that split, see [the web scraper API pattern](https://capzy.ai/blog/web-scraper-api-pattern) and [structured data extraction from HTML to JSON](https://capzy.ai/blog/structured-data-extraction-html-to-json). The part you cannot see in the request shape is what powers it. Getting automation past modern bot defenses is Capzy's core business, and the Data API rides on the same engine as our solving and scraping products. That matters because extraction from well-defended sites is not a solved problem you set up once; it is an arms race, and this is the rare product category where the vendor's day job is fighting it. # Getting started Pick the platform you already know you need, open its endpoint docs, and make one call with `freshness: "auto"`. If the JSON that comes back has the fields your feature needs, you have just replaced a scraper you will never have to write. Start with one endpoint and let the catalog grow into your roadmap, not ahead of it.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>A Data API is a REST API that returns structured data from websites through documented endpoints. Instead of fetching HTML and parsing it yourself, you call something like <code>amazon/search</code> or <code>google/search</code> with a few parameters, and you get back JSON with named fields: titles, prices, ratings, positions, links. Everything between your request and that JSON, including fetching, rendering, getting past anti-bot checks, and parsing, happens on the other side of the endpoint.</p> <p>That is the whole idea, and it is worth being precise about because "we have data" can mean a lot of things. A Data API is not a dataset dump, not a browser you drive, and not a generic proxy layer. It is a catalog of per-site, per-action endpoints with stable schemas you can build product features on.</p> <h1>The problem it replaces</h1> <p>If you have ever maintained your own scraper for a major site, you know the lifecycle. Week one, it works. Week three, a markup change silently breaks your price field. Week six, the site starts challenging your traffic and you are suddenly in the browser-automation and fingerprinting business instead of whatever business you meant to be in. We wrote about that grind in <a href="https://capzy.ai/blog/why-you-get-blocked-bot-signals">why you get blocked</a> and <a href="https://capzy.ai/blog/build-a-resilient-scraper">building a resilient scraper</a>.</p> <p>The sites with the most valuable data are also the hardest ones to scrape. That is not a coincidence: demand attracts defenses. So teams end up spending their scraping effort on the exact targets where the maintenance burden is highest.</p> <p>A Data API moves that burden across the API boundary. When a site changes its markup or rotates its defenses, the fix ships on our side and your integration does not change. Your code keeps calling the same endpoint and parsing the same fields.</p> <h1>How the Capzy Data API works</h1> <p>Every endpoint follows one request shape:</p> <pre><code>curl -X POST https://api.capzy.ai/v1/data/amazon/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "wireless earbuds", "domain": "com", "page": 1}, "freshness": "auto" }' </code></pre> <p>The platform and action live in the URL, the endpoint's parameters go in <code>params</code>, and the response is parsed JSON:</p> <pre><code>{ "errorId": 0, "status": "ready", "results": [ { "position": 1, "asin": "B0H5W39V6T", "title": "Wireless Earbuds, Sports Bluetooth Headphones...", "domain": "com" } ] } </code></pre> <p>Learn one endpoint and you have learned them all: <code>zillow/search</code>, <code>youtube/search</code>, <code>reddit/posts</code>, and the rest of the catalog behave the same way, with the same envelope and the same conventions. Long-running pulls return a <code>jobId</code> you can poll on <code>GET /v1/data/jobs/{job_id}</code>, so nothing forces you to hold a connection open.</p> <h1>What you can pull from it</h1> <p>The catalog covers 27 platforms with more than 300 documented endpoints, grouped roughly like this:</p> <ul> <li><strong>Commerce:</strong> Amazon (search, product detail, offers, reviews, bestsellers, deals, sellers, across 20 marketplaces), eBay, Walmart, Vinted, Shopee, Depop</li> <li><strong>Search:</strong> Google across its verticals (web, images, news, maps, shopping, scholar, trends, jobs, finance), plus Bing, DuckDuckGo, and Baidu</li> <li><strong>Social and video:</strong> YouTube, Twitter/X, TikTok, Reddit, Instagram, Facebook, LinkedIn public pages</li> <li><strong>Real estate:</strong> Zillow, Realtor, LoopNet, Immobiliare, Leboncoin, <a href="http://Apartments.com">Apartments.com</a></li> <li><strong>AI answers:</strong> ask endpoints that capture what AI assistants say, useful for brand monitoring</li> </ul> <p>Each endpoint documents its parameters and its response schema, so you know the field names before you make the first call, and they stay put afterward.</p> <h1>Freshness: cached, auto, or live</h1> <p>Every call takes a <code>freshness</code> mode, and it is one of the most practically useful parts of the API:</p> <ul> <li><code>fresh</code> forces a live pull from the site right now</li> <li><code>cached</code> serves a recent stored record when one exists</li> <li><code>auto</code> uses a recent record when it is new enough for that endpoint and pulls live otherwise</li> </ul> <p>Identical requests that are in flight at the same moment are coalesced into a single pull. If your dashboard fires the same product lookup five times in one minute, the site sees one visit and your five callers all get the result. You choose per request how fresh the data needs to be, which is a much better dial than "re-scrape everything, always".</p> <h1>Beyond single calls</h1> <p>The API is the core, but most real pipelines need a little machinery around it, so that machinery is built in:</p> <ul> <li><strong>Batch requests</strong> submit many queries against one endpoint in a single call, each tracked as its own job</li> <li><strong>Saved tasks</strong> store a configured request (platform, action, params, freshness) so you can re-run it in one click or on a schedule</li> <li><strong>Request history</strong> keeps every run with its status and a result snapshot you can review later</li> <li><strong>Exports</strong> turn any run's records into CSV, JSON, or XLSX from the console, no export script required</li> </ul> <p>If your workflow is "pull this every morning and hand the spreadsheet to the team", that is a saved task, a schedule, and an export button, not a service you have to write.</p> <h1>Where it fits in your stack</h1> <p>A Data API is the right tool when the sites you need are major platforms and what you want is their data, not a scraping project. If your target is a long-tail site the catalog does not cover, or you need custom crawl logic, a general-purpose scraping approach still makes sense, and the two compose well: catalog endpoints for the head of your workload, custom scraping for the tail. For the architectural view of that split, see <a href="https://capzy.ai/blog/web-scraper-api-pattern">the web scraper API pattern</a> and <a href="https://capzy.ai/blog/structured-data-extraction-html-to-json">structured data extraction from HTML to JSON</a>.</p> <p>The part you cannot see in the request shape is what powers it. Getting automation past modern bot defenses is Capzy's core business, and the Data API rides on the same engine as our solving and scraping products. That matters because extraction from well-defended sites is not a solved problem you set up once; it is an arms race, and this is the rare product category where the vendor's day job is fighting it.</p> <h1>Getting started</h1> <p>Pick the platform you already know you need, open its endpoint docs, and make one call with <code>freshness: "auto"</code>. If the JSON that comes back has the fields your feature needs, you have just replaced a scraper you will never have to write. Start with one endpoint and let the catalog grow into your roadmap, not ahead of it.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/what-is-a-data-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vol52h/what_is_a_data_api_structured_json_from_the_webs/",
"url_overridden_by_dest": "https://capzy.ai/blog/what-is-a-data-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T22:10:30+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/ppQPkUljhX_yHJFjUaiMcUnigKHCfgGOQ3UGmwS3s2A.jpeg?width=140&height=73&auto=webp&s=cb115fa1fc10007971145b43ba01a80b2463d4c6",
"preview_image": "https://external-preview.redd.it/ppQPkUljhX_yHJFjUaiMcUnigKHCfgGOQ3UGmwS3s2A.jpeg?auto=webp&s=e16b1c775ecdc9ab5d9bb71e7b2de66d0fbe8cc9",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vol4tu",
"fullname": "t3_1vol4tu",
"scraped_at": "2026-08-18T09:36:55.069369+00:00",
"title": "How to Get Amazon Product Data as JSON With One API Call",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "The fastest way to get Amazon product data as JSON is to call an endpoint that has already done the scraping, rendering, and parsing, and just hands you fields. This guide walks the Capzy Data API's Amazon coverage end to end: search, product detail, reviews, offers, bestsellers, and sellers, with real request and response shapes you can copy. # Why scraping Amazon yourself is a grind Amazon is one of the most-scraped sites on the internet, and it behaves accordingly. Product pages come in dozens of markup variants that change without notice. Listings render differently by region, device, and test bucket. And traffic that looks automated gets challenged early and often; we covered that side of the problem in [Amazon captchas and scraping](https://capzy.ai/blog/amazon-captcha-scraping). None of that work is differentiating for your product. The data is the point: titles, prices, ratings, availability. The endpoint layer exists so you can start at the data. # Search: from query to product list `amazon/search` takes a query and a marketplace and returns ranked results: curl -X POST https://api.capzy.ai/v1/data/amazon/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "wireless earbuds", "domain": "com", "page": 1}, "freshness": "auto" }' Each result row carries the product's `asin`, its `position` in the results, the `title`, and the marketplace `domain`. The `asin` is the key that unlocks every other Amazon endpoint, so a common pattern is search first, then fan out. # Product detail: the whole page, parsed `amazon/product_detail` takes an ASIN and returns the fields you would otherwise dig out of the page: import requests resp = requests.post( "https://api.capzy.ai/v1/data/amazon/product_detail", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"asin": "B0CHWRXH8B", "domain": "com"}, "freshness": "fresh", }, ) print(resp.json()) A trimmed real response: { "asin": "B0CHWRXH8B", "title": "Apple AirPods Pro (2nd Generation) Wireless Ear Buds...", "brand": "Apple Store", "price": 163.19, "currency": "USD", "rating": 4.7, "reviews_count": 28856, "availability": "Only 1 left in stock - order soon." } Note the `freshness: "fresh"` there: for a price or availability check you usually want a live pull. For enrichment jobs where an hour-old record is fine, `auto` avoids hammering the same page. # The rest of the Amazon family The same request shape covers the whole surface: * `amazon/reviews`: paginated reviews for an ASIN * `amazon/offers`: the buy-box and competing offers for a listing * `amazon/bestsellers` and `amazon/new_releases`: category charts * `amazon/deals`: current deal listings * `amazon/seller`**,** `amazon/seller_products`**,** `amazon/seller_feedback`: storefront data for marketplace sellers * `amazon/category` **and** `amazon/categories`: category browse trees * `amazon/autocomplete`: search suggestions for a prefix If you have ever built keyword tooling, the autocomplete endpoint alone is a feature: it is the same suggestion stream shoppers see as they type. # Going international Every Amazon endpoint takes a `domain` parameter that selects the marketplace: `com` for the US, [`co.uk`](http://co.uk), `de`, `fr`, [`co.jp`](http://co.jp), [`com.au`](http://com.au), and the rest of the 20 supported marketplaces. The response schema stays identical across all of them, so a price-comparison feature across five countries is the same code running five parameter sets, not five scrapers. # Keeping a catalog fresh without re-pulling everything Real Amazon workloads are rarely one call. Three features carry the load at volume: * **Batch requests**: send many ASINs against `product_detail` in one call, and each query is tracked as its own job * **Freshness modes**: `cached` and `auto` let enrichment reads reuse recent records instead of hitting the live site for every caller * **Saved tasks and schedules**: store "bestsellers in this category, every morning" once, then collect the runs from your request history or export them as CSV, JSON, or XLSX # A tiny end-to-end example Search for a product, take the top result, and pull its full detail: import requests BASE = "https://api.capzy.ai/v1/data" KEY = "YOUR_DATA_API_KEY" search = requests.post(f"{BASE}/amazon/search", json={ "clientKey": KEY, "params": {"query": "mechanical keyboard", "domain": "com", "page": 1}, "freshness": "auto", }).json() top = search["results"][0] detail = requests.post(f"{BASE}/amazon/product_detail", json={ "clientKey": KEY, "params": {"asin": top["asin"], "domain": "com"}, "freshness": "fresh", }).json() print(top["asin"], detail["title"], detail.get("price")) That is the entire integration. No headless browser, no HTML, no selectors, and nothing to fix when the page changes next month. # Wrap-up Amazon data as JSON is a solved problem when the parsing lives behind an endpoint. Start with `amazon/search` for discovery, `amazon/product_detail` for depth, and layer reviews, offers, and seller endpoints as your feature needs them. The full parameter and schema reference for every endpoint lives in the Data API docs.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>The fastest way to get Amazon product data as JSON is to call an endpoint that has already done the scraping, rendering, and parsing, and just hands you fields. This guide walks the Capzy Data API's Amazon coverage end to end: search, product detail, reviews, offers, bestsellers, and sellers, with real request and response shapes you can copy.</p> <h1>Why scraping Amazon yourself is a grind</h1> <p>Amazon is one of the most-scraped sites on the internet, and it behaves accordingly. Product pages come in dozens of markup variants that change without notice. Listings render differently by region, device, and test bucket. And traffic that looks automated gets challenged early and often; we covered that side of the problem in <a href="https://capzy.ai/blog/amazon-captcha-scraping">Amazon captchas and scraping</a>.</p> <p>None of that work is differentiating for your product. The data is the point: titles, prices, ratings, availability. The endpoint layer exists so you can start at the data.</p> <h1>Search: from query to product list</h1> <p><code>amazon/search</code> takes a query and a marketplace and returns ranked results:</p> <pre><code>curl -X POST https://api.capzy.ai/v1/data/amazon/search \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_DATA_API_KEY", "params": {"query": "wireless earbuds", "domain": "com", "page": 1}, "freshness": "auto" }' </code></pre> <p>Each result row carries the product's <code>asin</code>, its <code>position</code> in the results, the <code>title</code>, and the marketplace <code>domain</code>. The <code>asin</code> is the key that unlocks every other Amazon endpoint, so a common pattern is search first, then fan out.</p> <h1>Product detail: the whole page, parsed</h1> <p><code>amazon/product_detail</code> takes an ASIN and returns the fields you would otherwise dig out of the page:</p> <pre><code>import requests resp = requests.post( "https://api.capzy.ai/v1/data/amazon/product_detail", json={ "clientKey": "YOUR_DATA_API_KEY", "params": {"asin": "B0CHWRXH8B", "domain": "com"}, "freshness": "fresh", }, ) print(resp.json()) </code></pre> <p>A trimmed real response:</p> <pre><code>{ "asin": "B0CHWRXH8B", "title": "Apple AirPods Pro (2nd Generation) Wireless Ear Buds...", "brand": "Apple Store", "price": 163.19, "currency": "USD", "rating": 4.7, "reviews_count": 28856, "availability": "Only 1 left in stock - order soon." } </code></pre> <p>Note the <code>freshness: "fresh"</code> there: for a price or availability check you usually want a live pull. For enrichment jobs where an hour-old record is fine, <code>auto</code> avoids hammering the same page.</p> <h1>The rest of the Amazon family</h1> <p>The same request shape covers the whole surface:</p> <ul> <li><code>amazon/reviews</code>: paginated reviews for an ASIN</li> <li><code>amazon/offers</code>: the buy-box and competing offers for a listing</li> <li><code>amazon/bestsellers</code> and <code>amazon/new_releases</code>: category charts</li> <li><code>amazon/deals</code>: current deal listings</li> <li><code>amazon/seller</code><strong>,</strong> <code>amazon/seller_products</code><strong>,</strong> <code>amazon/seller_feedback</code>: storefront data for marketplace sellers</li> <li><code>amazon/category</code> <strong>and</strong> <code>amazon/categories</code>: category browse trees</li> <li><code>amazon/autocomplete</code>: search suggestions for a prefix</li> </ul> <p>If you have ever built keyword tooling, the autocomplete endpoint alone is a feature: it is the same suggestion stream shoppers see as they type.</p> <h1>Going international</h1> <p>Every Amazon endpoint takes a <code>domain</code> parameter that selects the marketplace: <code>com</code> for the US, <a href="http://co.uk"><code>co.uk</code></a>, <code>de</code>, <code>fr</code>, <a href="http://co.jp"><code>co.jp</code></a>, <a href="http://com.au"><code>com.au</code></a>, and the rest of the 20 supported marketplaces. The response schema stays identical across all of them, so a price-comparison feature across five countries is the same code running five parameter sets, not five scrapers.</p> <h1>Keeping a catalog fresh without re-pulling everything</h1> <p>Real Amazon workloads are rarely one call. Three features carry the load at volume:</p> <ul> <li><strong>Batch requests</strong>: send many ASINs against <code>product_detail</code> in one call, and each query is tracked as its own job</li> <li><strong>Freshness modes</strong>: <code>cached</code> and <code>auto</code> let enrichment reads reuse recent records instead of hitting the live site for every caller</li> <li><strong>Saved tasks and schedules</strong>: store "bestsellers in this category, every morning" once, then collect the runs from your request history or export them as CSV, JSON, or XLSX</li> </ul> <h1>A tiny end-to-end example</h1> <p>Search for a product, take the top result, and pull its full detail:</p> <pre><code>import requests BASE = "https://api.capzy.ai/v1/data" KEY = "YOUR_DATA_API_KEY" search = requests.post(f"{BASE}/amazon/search", json={ "clientKey": KEY, "params": {"query": "mechanical keyboard", "domain": "com", "page": 1}, "freshness": "auto", }).json() top = search["results"][0] detail = requests.post(f"{BASE}/amazon/product_detail", json={ "clientKey": KEY, "params": {"asin": top["asin"], "domain": "com"}, "freshness": "fresh", }).json() print(top["asin"], detail["title"], detail.get("price")) </code></pre> <p>That is the entire integration. No headless browser, no HTML, no selectors, and nothing to fix when the page changes next month.</p> <h1>Wrap-up</h1> <p>Amazon data as JSON is a solved problem when the parsing lives behind an endpoint. Start with <code>amazon/search</code> for discovery, <code>amazon/product_detail</code> for depth, and layer reviews, offers, and seller endpoints as your feature needs them. The full parameter and schema reference for every endpoint lives in the Data API docs.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/get-amazon-product-data-json-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vol4tu/how_to_get_amazon_product_data_as_json_with_one/",
"url_overridden_by_dest": "https://capzy.ai/blog/get-amazon-product-data-json-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T22:10:13+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/eljwbR9g2_N58Qgr8Q1Ax_o5vwJ7sODbNTC0LacG1xI.jpeg?width=140&height=73&auto=webp&s=75ef584da2fbda7a4371d79a0363c202de8cef53",
"preview_image": "https://external-preview.redd.it/eljwbR9g2_N58Qgr8Q1Ax_o5vwJ7sODbNTC0LacG1xI.jpeg?auto=webp&s=11915280273dc626033d1591a8b349b45a1c1277",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vol44l",
"fullname": "t3_1vol44l",
"scraped_at": "2026-08-18T09:36:55.069378+00:00",
"title": "What Can You Build With a Data API? Seven Real Pipelines",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "A structured data API sounds abstract until you see what teams actually run on it. Below are seven pipelines we see in production, each with the endpoints it uses and the shape of the workflow. They all share one property: the hard part (getting clean data out of well-defended sites) is behind the API, so the pipeline itself is small. # 1. Price and availability monitoring The classic. A retailer or brand watches its own listings and the surrounding market across marketplaces. * **Endpoints:** `amazon/product_detail`, `amazon/offers`, `walmart/products`, `ebay/items` * **Workflow:** a saved task per product set, scheduled daily or hourly, with `freshness: "fresh"` because stale prices defeat the purpose. Batch requests keep large catalogs to a handful of calls, and the run history becomes your price timeline. We went deep on this pattern in [scraping e-commerce prices at scale](https://capzy.ai/blog/scraping-ecommerce-prices-at-scale). # 2. Search rank tracking Where do you, and everyone you compete with, show up for the queries that matter? * **Endpoints:** `google/search`, `bing/search`, `duckduckgo/search` * **Workflow:** one scheduled run per keyword set per engine. Each result row carries its position, so rank-over-time is just plotting the same query across daily runs. The [SERP scraping guide](https://capzy.ai/blog/scraping-search-results-serp) covers why doing this against the live engines yourself is harder than it looks. # 3. Brand and sentiment listening What are people saying about your product right now, and where? * **Endpoints:** `twitter/search`, `reddit/search`, `youtube/search` plus comments, `tiktok/search` * **Workflow:** scheduled searches for your brand terms, new results diffed against the last run, and mentions routed to wherever your team lives. Reddit and YouTube comment threads are where the honest feedback hides. # 4. Real estate market feeds Listings, price cuts, and inventory across markets, without an agent portal login. * **Endpoints:** `zillow/search` and `zillow/property`, `realtor/search`, `leboncoin/search`, `immobiliare/search`, `loopnet/search` for commercial * **Workflow:** a saved search per market segment on a daily schedule, exported as XLSX for the analysts. Cross-country coverage means one pipeline shape serves the US, France, and Italy teams. # 5. Marketplace seller intelligence Who sells against you, what do they list, and how are they rated? * **Endpoints:** `amazon/seller`, `amazon/seller_products`, `amazon/seller_feedback`, `ebay/sellers` * **Workflow:** start from the offers on your key listings, resolve the seller IDs, then walk their storefronts on a weekly schedule. This is the pipeline that turns "someone is undercutting us" from a hunch into a table. # 6. Content and trend research What is rising, what is peaking, and what should you make next? * **Endpoints:** `youtube/trends`, `google/trends`, `tiktok/trending`, `reddit/subreddits` and top posts * **Workflow:** weekly scheduled pulls feeding a research dashboard. Because every endpoint returns the same envelope, adding a platform to the dashboard is one more saved task, not a new integration. # 7. AI answer monitoring A new one, and growing fast: when someone asks an AI assistant about your category, does your brand appear, and what does the answer say? * **Endpoints:** `chatgpt/ask` and the brand-visibility endpoints * **Workflow:** a scheduled set of category questions, with answers and citations captured as structured records. Marketing teams treat this like rank tracking for the AI era. # The pattern behind all seven Look at the workflows again and the same skeleton appears every time: * **A saved task** defines the pull: platform, action, params, freshness * **A schedule** runs it on cadence, with batch requests handling volume * **Freshness modes** decide which reads hit the live site and which reuse recent records * **History and exports** turn runs into timelines, spreadsheets, and downstream feeds None of these pipelines needed a scraper, a browser fleet, or a parsing layer. That is the real pitch for a Data API: the moat work (staying unblocked and correctly parsed on major sites, which is Capzy's core business) sits behind the endpoint, and what is left for you is genuinely small. # Start with one If one of the seven matched a thing your team already wants, the path is short: find the endpoint in the Data API docs, make one call with `freshness: "auto"`, and check the fields. The first pipeline usually ships in an afternoon, and the second one reuses everything you learned in the first.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>A structured data API sounds abstract until you see what teams actually run on it. Below are seven pipelines we see in production, each with the endpoints it uses and the shape of the workflow. They all share one property: the hard part (getting clean data out of well-defended sites) is behind the API, so the pipeline itself is small.</p> <h1>1. Price and availability monitoring</h1> <p>The classic. A retailer or brand watches its own listings and the surrounding market across marketplaces.</p> <ul> <li><strong>Endpoints:</strong> <code>amazon/product_detail</code>, <code>amazon/offers</code>, <code>walmart/products</code>, <code>ebay/items</code></li> <li><strong>Workflow:</strong> a saved task per product set, scheduled daily or hourly, with <code>freshness: "fresh"</code> because stale prices defeat the purpose. Batch requests keep large catalogs to a handful of calls, and the run history becomes your price timeline.</li> </ul> <p>We went deep on this pattern in <a href="https://capzy.ai/blog/scraping-ecommerce-prices-at-scale">scraping e-commerce prices at scale</a>.</p> <h1>2. Search rank tracking</h1> <p>Where do you, and everyone you compete with, show up for the queries that matter?</p> <ul> <li><strong>Endpoints:</strong> <code>google/search</code>, <code>bing/search</code>, <code>duckduckgo/search</code></li> <li><strong>Workflow:</strong> one scheduled run per keyword set per engine. Each result row carries its position, so rank-over-time is just plotting the same query across daily runs. The <a href="https://capzy.ai/blog/scraping-search-results-serp">SERP scraping guide</a> covers why doing this against the live engines yourself is harder than it looks.</li> </ul> <h1>3. Brand and sentiment listening</h1> <p>What are people saying about your product right now, and where?</p> <ul> <li><strong>Endpoints:</strong> <code>twitter/search</code>, <code>reddit/search</code>, <code>youtube/search</code> plus comments, <code>tiktok/search</code></li> <li><strong>Workflow:</strong> scheduled searches for your brand terms, new results diffed against the last run, and mentions routed to wherever your team lives. Reddit and YouTube comment threads are where the honest feedback hides.</li> </ul> <h1>4. Real estate market feeds</h1> <p>Listings, price cuts, and inventory across markets, without an agent portal login.</p> <ul> <li><strong>Endpoints:</strong> <code>zillow/search</code> and <code>zillow/property</code>, <code>realtor/search</code>, <code>leboncoin/search</code>, <code>immobiliare/search</code>, <code>loopnet/search</code> for commercial</li> <li><strong>Workflow:</strong> a saved search per market segment on a daily schedule, exported as XLSX for the analysts. Cross-country coverage means one pipeline shape serves the US, France, and Italy teams.</li> </ul> <h1>5. Marketplace seller intelligence</h1> <p>Who sells against you, what do they list, and how are they rated?</p> <ul> <li><strong>Endpoints:</strong> <code>amazon/seller</code>, <code>amazon/seller_products</code>, <code>amazon/seller_feedback</code>, <code>ebay/sellers</code></li> <li><strong>Workflow:</strong> start from the offers on your key listings, resolve the seller IDs, then walk their storefronts on a weekly schedule. This is the pipeline that turns "someone is undercutting us" from a hunch into a table.</li> </ul> <h1>6. Content and trend research</h1> <p>What is rising, what is peaking, and what should you make next?</p> <ul> <li><strong>Endpoints:</strong> <code>youtube/trends</code>, <code>google/trends</code>, <code>tiktok/trending</code>, <code>reddit/subreddits</code> and top posts</li> <li><strong>Workflow:</strong> weekly scheduled pulls feeding a research dashboard. Because every endpoint returns the same envelope, adding a platform to the dashboard is one more saved task, not a new integration.</li> </ul> <h1>7. AI answer monitoring</h1> <p>A new one, and growing fast: when someone asks an AI assistant about your category, does your brand appear, and what does the answer say?</p> <ul> <li><strong>Endpoints:</strong> <code>chatgpt/ask</code> and the brand-visibility endpoints</li> <li><strong>Workflow:</strong> a scheduled set of category questions, with answers and citations captured as structured records. Marketing teams treat this like rank tracking for the AI era.</li> </ul> <h1>The pattern behind all seven</h1> <p>Look at the workflows again and the same skeleton appears every time:</p> <ul> <li><strong>A saved task</strong> defines the pull: platform, action, params, freshness</li> <li><strong>A schedule</strong> runs it on cadence, with batch requests handling volume</li> <li><strong>Freshness modes</strong> decide which reads hit the live site and which reuse recent records</li> <li><strong>History and exports</strong> turn runs into timelines, spreadsheets, and downstream feeds</li> </ul> <p>None of these pipelines needed a scraper, a browser fleet, or a parsing layer. That is the real pitch for a Data API: the moat work (staying unblocked and correctly parsed on major sites, which is Capzy's core business) sits behind the endpoint, and what is left for you is genuinely small.</p> <h1>Start with one</h1> <p>If one of the seven matched a thing your team already wants, the path is short: find the endpoint in the Data API docs, make one call with <code>freshness: "auto"</code>, and check the fields. The first pipeline usually ships in an afternoon, and the second one reuses everything you learned in the first.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/data-api-use-cases",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vol44l/what_can_you_build_with_a_data_api_seven_real/",
"url_overridden_by_dest": "https://capzy.ai/blog/data-api-use-cases",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-14T22:09:25+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/E8xqimQ8at25MooSCCglfuNFZ80_IPFGOlMlmq5UhsQ.jpeg?width=140&height=73&auto=webp&s=c25130ac0d31b5b24330dc5fa395b058c10a6327",
"preview_image": "https://external-preview.redd.it/E8xqimQ8at25MooSCCglfuNFZ80_IPFGOlMlmq5UhsQ.jpeg?auto=webp&s=f9c07e5246f0e3a1a39e94155f7f5617eb4205f4",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8vw0",
"fullname": "t3_1vh8vw0",
"scraped_at": "2026-08-18T09:36:55.069389+00:00",
"title": "What Is a CAPTCHA-Solving API? How It Works and When You Need One",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "A CAPTCHA-solving API takes a challenge that would normally stop your script, solves it, and hands back a token you can drop into the target site's form. That is the whole job. You send the site URL and the widget's public key, you wait a few seconds, and you get a string back that the site treats as proof a human passed the check. If you have ever watched a scraper or a checkout bot die at a Cloudflare Turnstile widget or a reCAPTCHA checkbox, this is the piece that gets it moving again. Below is what one of these APIs actually does under the hood, what the request and response look like, and the cases where reaching for one is the right call versus overkill. # What a CAPTCHA-solving API actually does Most modern captchas are not a single image of squiggly letters anymore. They are JavaScript widgets that run behavior checks, fingerprint your browser, and issue a signed token when they decide you are probably human. The token is the thing the site cares about. reCAPTCHA calls its field `g-recaptcha-response`. Cloudflare Turnstile uses `cf-turnstile-response`. hCaptcha uses `h-captcha-response`. When you submit the form, the site's backend calls the captcha vendor's `siteverify` endpoint to confirm the token is real and fresh. A solving API produces that token for you. Internally it does one of two things depending on the captcha type: * For behavioral widgets (Turnstile, reCAPTCHA v3, most invisible challenges) it runs a real browser with a coherent fingerprint, lets the widget execute, and captures the token the widget mints. * For image or interactive challenges (the "select all traffic lights" grid, sliders, rotate puzzles) it either uses computer-vision models or drives the puzzle to completion, then returns the resulting token. You never see any of that. You get a clean async job: submit a task, poll for the result. # What the request and response look like The typical flow is two calls. First you create a task with the site details. Then you poll a second endpoint until the result is ready. Here is a minimal example against Capzy for a reCAPTCHA v2 widget: # 1) create the task curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "RecaptchaV2Task", "websiteURL": "https://example.com/login", "websiteKey": "6Lc_aQ0AAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } }' # -> {"taskId": "a1b2c3d4-..."} Then poll for the token: # 2) fetch the result curl -s https://api.capzy.ai/getTaskResult \ -H "Content-Type: application/json" \ -d '{"clientKey":"YOUR_API_KEY","taskId":"a1b2c3d4-..."}' # -> {"status":"ready","solution":{"gRecaptchaResponse":"03AGdBq26..."}} The `websiteKey` is the public sitekey baked into the page's HTML. For reCAPTCHA it starts with `6L`. For Turnstile it looks like `0x4AAAAAAA...`. You are not sending a secret, just the same public key any visitor's browser sees. Once you have `gRecaptchaResponse`, you inject it into the page's hidden input and submit, or you pass it straight to whatever backend endpoint the form posts to. >The token is short-lived and origin-bound. A reCAPTCHA token is typically valid for about two minutes and only for the domain it was minted on. Solve it, use it, move on. Do not cache tokens across sites. # When you actually need one You need a solving API when a captcha stands between your automation and the data or action you are after, and the captcha is not something you can silently pass by looking human enough. That last part matters. A lot of blocks are not really captcha problems. Reach for a solving API when: * A visible interactive challenge appears (image grid, slider, press-and-hold) that your headless browser cannot complete on its own. * An invisible score-based widget keeps returning a failing score no matter how clean your session looks, and you need a token minted by infrastructure the vendor trusts. * You are running at volume and cannot babysit each challenge by hand. You probably do not need one when the "captcha" is really an IP reputation block or a fingerprint mismatch. If a site throws challenges only from datacenter ranges, the fix is a cleaner egress IP, not a solver. If it flags you because your headless browser leaks automation signals, the fix is a real browser with a consistent fingerprint. Solving the token still leaves you looking wrong on every other signal. That is why the honest version of this advice is: a token is necessary but often not sufficient. The three things that get you the rest of the way are clean [Proxies](https://capzy.ai/proxies) so your requests come from residential-quality egress, a real [Cloud Browser](https://capzy.ai/browser) when the site needs a genuine engine, and a coherent [Fingerprint API](https://capzy.ai/fingerprints) so your headers, canvas, and navigator properties agree with each other. The solver handles the challenge; the rest handles the trust. # Picking a solver for your captcha type Different captchas need different task types. A checkbox reCAPTCHA is not the same task as an invisible v3 score, and Turnstile is its own thing entirely. The full list of what Capzy supports lives in the [solver catalog](https://capzy.ai/solvers), where each captcha has its own task type and example payload. If you are dealing specifically with reCAPTCHA v2, the [reCAPTCHA v2 solver](https://capzy.ai/solvers/recaptcha-v2) page walks through the exact fields. For Cloudflare's widget, see the [Turnstile solver](https://capzy.ai/solvers/turnstile). A few practical notes on choosing: * Match the task type to the widget, not to what the page looks like. Many pages render an invisible challenge that only shows a grid on fallback. * Pass the real `websiteURL`, including the path if the sitekey is path-scoped. Some deployments key the challenge to the exact page. * For score-based captchas, the action name and page context can affect the score you get back. Send them if the vendor supports it. # The short version A CAPTCHA-solving API is a token factory: send the sitekey and URL, get back a valid response string, submit it. It is the right tool when a genuine challenge blocks you and the wrong tool when your real problem is IP reputation or a leaky fingerprint. Most reliable pipelines use a solver together with clean IPs and a real browser, because a token alone does not make the rest of your session look human. Ready to try it? Grab a key with a [free Capzy account](https://capzy.ai/auth/register) and point your first `createTask` call at the [solver catalog](https://capzy.ai/solvers) to see which task type fits your target.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>A CAPTCHA-solving API takes a challenge that would normally stop your script, solves it, and hands back a token you can drop into the target site's form. That is the whole job. You send the site URL and the widget's public key, you wait a few seconds, and you get a string back that the site treats as proof a human passed the check.</p> <p>If you have ever watched a scraper or a checkout bot die at a Cloudflare Turnstile widget or a reCAPTCHA checkbox, this is the piece that gets it moving again. Below is what one of these APIs actually does under the hood, what the request and response look like, and the cases where reaching for one is the right call versus overkill.</p> <h1>What a CAPTCHA-solving API actually does</h1> <p>Most modern captchas are not a single image of squiggly letters anymore. They are JavaScript widgets that run behavior checks, fingerprint your browser, and issue a signed token when they decide you are probably human. The token is the thing the site cares about. reCAPTCHA calls its field <code>g-recaptcha-response</code>. Cloudflare Turnstile uses <code>cf-turnstile-response</code>. hCaptcha uses <code>h-captcha-response</code>. When you submit the form, the site's backend calls the captcha vendor's <code>siteverify</code> endpoint to confirm the token is real and fresh.</p> <p>A solving API produces that token for you. Internally it does one of two things depending on the captcha type:</p> <ul> <li>For behavioral widgets (Turnstile, reCAPTCHA v3, most invisible challenges) it runs a real browser with a coherent fingerprint, lets the widget execute, and captures the token the widget mints.</li> <li>For image or interactive challenges (the "select all traffic lights" grid, sliders, rotate puzzles) it either uses computer-vision models or drives the puzzle to completion, then returns the resulting token.</li> </ul> <p>You never see any of that. You get a clean async job: submit a task, poll for the result.</p> <h1>What the request and response look like</h1> <p>The typical flow is two calls. First you create a task with the site details. Then you poll a second endpoint until the result is ready. Here is a minimal example against Capzy for a reCAPTCHA v2 widget:</p> <pre><code># 1) create the task curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "RecaptchaV2Task", "websiteURL": "https://example.com/login", "websiteKey": "6Lc_aQ0AAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } }' # -> {"taskId": "a1b2c3d4-..."} </code></pre> <p>Then poll for the token:</p> <pre><code># 2) fetch the result curl -s https://api.capzy.ai/getTaskResult \ -H "Content-Type: application/json" \ -d '{"clientKey":"YOUR_API_KEY","taskId":"a1b2c3d4-..."}' # -> {"status":"ready","solution":{"gRecaptchaResponse":"03AGdBq26..."}} </code></pre> <p>The <code>websiteKey</code> is the public sitekey baked into the page's HTML. For reCAPTCHA it starts with <code>6L</code>. For Turnstile it looks like <code>0x4AAAAAAA...</code>. You are not sending a secret, just the same public key any visitor's browser sees. Once you have <code>gRecaptchaResponse</code>, you inject it into the page's hidden input and submit, or you pass it straight to whatever backend endpoint the form posts to.</p> <blockquote> <p>The token is short-lived and origin-bound. A reCAPTCHA token is typically valid for about two minutes and only for the domain it was minted on. Solve it, use it, move on. Do not cache tokens across sites.</p> </blockquote> <h1>When you actually need one</h1> <p>You need a solving API when a captcha stands between your automation and the data or action you are after, and the captcha is not something you can silently pass by looking human enough. That last part matters. A lot of blocks are not really captcha problems.</p> <p>Reach for a solving API when:</p> <ul> <li>A visible interactive challenge appears (image grid, slider, press-and-hold) that your headless browser cannot complete on its own.</li> <li>An invisible score-based widget keeps returning a failing score no matter how clean your session looks, and you need a token minted by infrastructure the vendor trusts.</li> <li>You are running at volume and cannot babysit each challenge by hand.</li> </ul> <p>You probably do not need one when the "captcha" is really an IP reputation block or a fingerprint mismatch. If a site throws challenges only from datacenter ranges, the fix is a cleaner egress IP, not a solver. If it flags you because your headless browser leaks automation signals, the fix is a real browser with a consistent fingerprint. Solving the token still leaves you looking wrong on every other signal.</p> <p>That is why the honest version of this advice is: a token is necessary but often not sufficient. The three things that get you the rest of the way are clean <a href="https://capzy.ai/proxies">Proxies</a> so your requests come from residential-quality egress, a real <a href="https://capzy.ai/browser">Cloud Browser</a> when the site needs a genuine engine, and a coherent <a href="https://capzy.ai/fingerprints">Fingerprint API</a> so your headers, canvas, and navigator properties agree with each other. The solver handles the challenge; the rest handles the trust.</p> <h1>Picking a solver for your captcha type</h1> <p>Different captchas need different task types. A checkbox reCAPTCHA is not the same task as an invisible v3 score, and Turnstile is its own thing entirely. The full list of what Capzy supports lives in the <a href="https://capzy.ai/solvers">solver catalog</a>, where each captcha has its own task type and example payload. If you are dealing specifically with reCAPTCHA v2, the <a href="https://capzy.ai/solvers/recaptcha-v2">reCAPTCHA v2 solver</a> page walks through the exact fields. For Cloudflare's widget, see the <a href="https://capzy.ai/solvers/turnstile">Turnstile solver</a>.</p> <p>A few practical notes on choosing:</p> <ul> <li>Match the task type to the widget, not to what the page looks like. Many pages render an invisible challenge that only shows a grid on fallback.</li> <li>Pass the real <code>websiteURL</code>, including the path if the sitekey is path-scoped. Some deployments key the challenge to the exact page.</li> <li>For score-based captchas, the action name and page context can affect the score you get back. Send them if the vendor supports it.</li> </ul> <h1>The short version</h1> <p>A CAPTCHA-solving API is a token factory: send the sitekey and URL, get back a valid response string, submit it. It is the right tool when a genuine challenge blocks you and the wrong tool when your real problem is IP reputation or a leaky fingerprint. Most reliable pipelines use a solver together with clean IPs and a real browser, because a token alone does not make the rest of your session look human.</p> <p>Ready to try it? Grab a key with a <a href="https://capzy.ai/auth/register">free Capzy account</a> and point your first <code>createTask</code> call at the <a href="https://capzy.ai/solvers">solver catalog</a> to see which task type fits your target.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/what-is-a-captcha-solving-api",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8vw0/what_is_a_captchasolving_api_how_it_works_and/",
"url_overridden_by_dest": "https://capzy.ai/blog/what-is-a-captcha-solving-api",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:19:12+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/y7waOPdcAz3W_eNhN5Ayd0ezeNVwpZD7_ypEX6hrWnY.jpeg?width=140&height=73&auto=webp&s=6dd0683b0af7f6b148f238eb4c74d6c44130c9ba",
"preview_image": "https://external-preview.redd.it/y7waOPdcAz3W_eNhN5Ayd0ezeNVwpZD7_ypEX6hrWnY.jpeg?auto=webp&s=e1ec19b627332d491f5fcfc9917b7d6dde5b9446",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8vh2",
"fullname": "t3_1vh8vh2",
"scraped_at": "2026-08-18T09:36:55.069396+00:00",
"title": "How to Solve Cloudflare Turnstile in 2026: A Developer's Guide",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "To solve Cloudflare Turnstile, you need a valid `cf-turnstile-response` token minted for the exact sitekey and origin the widget was placed on, and you need it to survive Cloudflare's server-side `siteverify` check. That is harder than it sounds in 2026, because Turnstile stopped being a checkbox a while ago and became a rolling behavioral challenge that watches your browser as much as your click. This guide covers what the widget actually does, why a token that looks fine still gets rejected, and how to get a working token with a solving API. There is a code example you can copy near the end. # What Turnstile is checking Turnstile is Cloudflare's replacement for the old "I am not a robot" checkbox. It runs in one of three modes: a managed challenge that may show a checkbox, a non-interactive mode that shows a spinner and passes silently, and an invisible mode with no visible UI at all. In every mode it does the same core work. It loads a JavaScript challenge, runs a proof-of-work and a battery of browser environment checks, times how you interact with the page, and then mints a token if it decides you are legitimate. The token lands in a hidden field named `cf-turnstile-response`. Your form submits it, and Cloudflare's backend calls its verify endpoint to confirm the token is real, unexpired, and matches the sitekey and hostname. The sitekey is public and lives right in the page HTML, usually as a `data-sitekey` attribute starting with `0x4AAAAAAA`. Here is the part that trips people up. The token is not the whole picture. Turnstile embeds signals about the environment it ran in. If you mint a token in a browser that leaks automation, or from an IP with a bad reputation, Cloudflare can still reject it at verify time or feed you a harder challenge next round. A token is a claim; Cloudflare decides whether to believe it. # Why your Turnstile tokens fail When a token gets rejected, it is almost always one of these: * **Wrong sitekey or origin.** Turnstile binds the token to the sitekey and the hostname it was issued for. Solve it against [`https://app.example.com`](https://app.example.com) and try to use it on [`https://www.example.com`](https://www.example.com) and it fails. * **Expired token.** Turnstile tokens are short-lived, on the order of a few minutes. Mint, submit, done. Do not queue them. * **Reused token.** Tokens are single-use. Cloudflare's verify endpoint remembers. * **Dirty egress IP.** Even a technically valid token gets a colder reception from a flagged datacenter range. Cloudflare correlates the token's context with the IP that submits the form. * **Fingerprint mismatch.** If the browser that minted the token looks nothing like the client that submits it, the signals do not line up. That last pair is why "I solved the captcha but I am still blocked" is such a common complaint. You solved the widget. You did not solve the session. >Turnstile grades the whole request, not just the click. A perfect token from a browser that leaks `navigator.webdriver` or from an IP that hosts a thousand other bots will still earn you a rougher ride. # Solving Turnstile with an API The reliable path is to let infrastructure that Cloudflare already trusts mint the token for you: a real browser engine with a coherent fingerprint, running from clean egress. A solving API wraps all of that. You submit the sitekey and page URL, it runs the challenge, and it returns `cf-turnstile-response`. Here is a minimal Python example against Capzy: import requests, time API = "https://api.capzy.ai" KEY = "YOUR_API_KEY" # create the task task = requests.post(f"{API}/createTask", json={ "clientKey": KEY, "task": { "type": "TurnstileTask", "websiteURL": "https://example.com/login", "websiteKey": "0x4AAAAAAABkMYinukE8nzY" } }).json() task_id = task["taskId"] # poll for the token while True: res = requests.post(f"{API}/getTaskResult", json={ "clientKey": KEY, "taskId": task_id }).json() if res["status"] == "ready": token = res["solution"]["token"] break time.sleep(3) print(token) # inject into cf-turnstile-response, then submit Once you have the token, drop it into the hidden `cf-turnstile-response` input on the page (or send it directly to whatever endpoint the form posts to) and submit within the token's lifetime. If the widget is invisible and re-fires on navigation, solve again per navigation rather than reusing the old token. For the exact field names and the Cloudflare-specific options like `action` and cData, the [Turnstile solver](https://capzy.ai/solvers/turnstile) page has the full task schema, and the broader [solver catalog](https://capzy.ai/solvers) covers the other Cloudflare-adjacent challenges you might hit on the same site. # Getting the rest of the session right If tokens are validating but you are still challenged repeatedly, the token is not your problem. Work the two signals Cloudflare correlates alongside it. First, your egress. Turnstile is materially friendlier to residential-quality IPs than to datacenter ranges that carry a reputation for automation. Routing through the [Proxies API](https://capzy.ai/proxies) so your form submits from a clean IP often does more for your pass rate than any change to the token itself. Second, your browser. If you are submitting from a raw HTTP client while the token was minted in a full browser, the mismatch is visible. Either drive the whole flow through a real [Cloud Browser](https://capzy.ai/browser), or make your client's fingerprint coherent with the [Fingerprint API](https://capzy.ai/fingerprints) so your user agent, client hints, and TLS all agree. Turnstile is looking for contradictions. Do not give it any. A quick checklist for a healthy Turnstile flow: * Solve against the exact `websiteURL` and `data-sitekey` from the live page. * Submit the token immediately, once, and never reuse it. * Submit from the same IP class and fingerprint profile the token was minted with. * Re-solve on navigation instead of caching. # Wrapping up Turnstile in 2026 is a behavioral, environment-aware challenge, and `cf-turnstile-response` is only the visible output. Get the token from trusted infrastructure, submit it fresh and once, and back it with a clean IP and a coherent fingerprint so the rest of your request agrees with the token. Start solving Turnstile today with a [free Capzy account](https://capzy.ai/auth/register), and pair it with the [Proxies API](https://capzy.ai/proxies) and [Cloud Browser](https://capzy.ai/browser) when a token alone is not enough.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>To solve Cloudflare Turnstile, you need a valid <code>cf-turnstile-response</code> token minted for the exact sitekey and origin the widget was placed on, and you need it to survive Cloudflare's server-side <code>siteverify</code> check. That is harder than it sounds in 2026, because Turnstile stopped being a checkbox a while ago and became a rolling behavioral challenge that watches your browser as much as your click.</p> <p>This guide covers what the widget actually does, why a token that looks fine still gets rejected, and how to get a working token with a solving API. There is a code example you can copy near the end.</p> <h1>What Turnstile is checking</h1> <p>Turnstile is Cloudflare's replacement for the old "I am not a robot" checkbox. It runs in one of three modes: a managed challenge that may show a checkbox, a non-interactive mode that shows a spinner and passes silently, and an invisible mode with no visible UI at all. In every mode it does the same core work. It loads a JavaScript challenge, runs a proof-of-work and a battery of browser environment checks, times how you interact with the page, and then mints a token if it decides you are legitimate.</p> <p>The token lands in a hidden field named <code>cf-turnstile-response</code>. Your form submits it, and Cloudflare's backend calls its verify endpoint to confirm the token is real, unexpired, and matches the sitekey and hostname. The sitekey is public and lives right in the page HTML, usually as a <code>data-sitekey</code> attribute starting with <code>0x4AAAAAAA</code>.</p> <p>Here is the part that trips people up. The token is not the whole picture. Turnstile embeds signals about the environment it ran in. If you mint a token in a browser that leaks automation, or from an IP with a bad reputation, Cloudflare can still reject it at verify time or feed you a harder challenge next round. A token is a claim; Cloudflare decides whether to believe it.</p> <h1>Why your Turnstile tokens fail</h1> <p>When a token gets rejected, it is almost always one of these:</p> <ul> <li><strong>Wrong sitekey or origin.</strong> Turnstile binds the token to the sitekey and the hostname it was issued for. Solve it against <a href="https://app.example.com"><code>https://app.example.com</code></a> and try to use it on <a href="https://www.example.com"><code>https://www.example.com</code></a> and it fails.</li> <li><strong>Expired token.</strong> Turnstile tokens are short-lived, on the order of a few minutes. Mint, submit, done. Do not queue them.</li> <li><strong>Reused token.</strong> Tokens are single-use. Cloudflare's verify endpoint remembers.</li> <li><strong>Dirty egress IP.</strong> Even a technically valid token gets a colder reception from a flagged datacenter range. Cloudflare correlates the token's context with the IP that submits the form.</li> <li><strong>Fingerprint mismatch.</strong> If the browser that minted the token looks nothing like the client that submits it, the signals do not line up.</li> </ul> <p>That last pair is why "I solved the captcha but I am still blocked" is such a common complaint. You solved the widget. You did not solve the session.</p> <blockquote> <p>Turnstile grades the whole request, not just the click. A perfect token from a browser that leaks <code>navigator.webdriver</code> or from an IP that hosts a thousand other bots will still earn you a rougher ride.</p> </blockquote> <h1>Solving Turnstile with an API</h1> <p>The reliable path is to let infrastructure that Cloudflare already trusts mint the token for you: a real browser engine with a coherent fingerprint, running from clean egress. A solving API wraps all of that. You submit the sitekey and page URL, it runs the challenge, and it returns <code>cf-turnstile-response</code>.</p> <p>Here is a minimal Python example against Capzy:</p> <pre><code>import requests, time API = "https://api.capzy.ai" KEY = "YOUR_API_KEY" # create the task task = requests.post(f"{API}/createTask", json={ "clientKey": KEY, "task": { "type": "TurnstileTask", "websiteURL": "https://example.com/login", "websiteKey": "0x4AAAAAAABkMYinukE8nzY" } }).json() task_id = task["taskId"] # poll for the token while True: res = requests.post(f"{API}/getTaskResult", json={ "clientKey": KEY, "taskId": task_id }).json() if res["status"] == "ready": token = res["solution"]["token"] break time.sleep(3) print(token) # inject into cf-turnstile-response, then submit </code></pre> <p>Once you have the token, drop it into the hidden <code>cf-turnstile-response</code> input on the page (or send it directly to whatever endpoint the form posts to) and submit within the token's lifetime. If the widget is invisible and re-fires on navigation, solve again per navigation rather than reusing the old token.</p> <p>For the exact field names and the Cloudflare-specific options like <code>action</code> and cData, the <a href="https://capzy.ai/solvers/turnstile">Turnstile solver</a> page has the full task schema, and the broader <a href="https://capzy.ai/solvers">solver catalog</a> covers the other Cloudflare-adjacent challenges you might hit on the same site.</p> <h1>Getting the rest of the session right</h1> <p>If tokens are validating but you are still challenged repeatedly, the token is not your problem. Work the two signals Cloudflare correlates alongside it.</p> <p>First, your egress. Turnstile is materially friendlier to residential-quality IPs than to datacenter ranges that carry a reputation for automation. Routing through the <a href="https://capzy.ai/proxies">Proxies API</a> so your form submits from a clean IP often does more for your pass rate than any change to the token itself.</p> <p>Second, your browser. If you are submitting from a raw HTTP client while the token was minted in a full browser, the mismatch is visible. Either drive the whole flow through a real <a href="https://capzy.ai/browser">Cloud Browser</a>, or make your client's fingerprint coherent with the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> so your user agent, client hints, and TLS all agree. Turnstile is looking for contradictions. Do not give it any.</p> <p>A quick checklist for a healthy Turnstile flow:</p> <ul> <li>Solve against the exact <code>websiteURL</code> and <code>data-sitekey</code> from the live page.</li> <li>Submit the token immediately, once, and never reuse it.</li> <li>Submit from the same IP class and fingerprint profile the token was minted with.</li> <li>Re-solve on navigation instead of caching.</li> </ul> <h1>Wrapping up</h1> <p>Turnstile in 2026 is a behavioral, environment-aware challenge, and <code>cf-turnstile-response</code> is only the visible output. Get the token from trusted infrastructure, submit it fresh and once, and back it with a clean IP and a coherent fingerprint so the rest of your request agrees with the token.</p> <p>Start solving Turnstile today with a <a href="https://capzy.ai/auth/register">free Capzy account</a>, and pair it with the <a href="https://capzy.ai/proxies">Proxies API</a> and <a href="https://capzy.ai/browser">Cloud Browser</a> when a token alone is not enough.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/solve-cloudflare-turnstile-2026-guide",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8vh2/how_to_solve_cloudflare_turnstile_in_2026_a/",
"url_overridden_by_dest": "https://capzy.ai/blog/solve-cloudflare-turnstile-2026-guide",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:18:46+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/nv7JXdLRyJr_Uwgl5OCJQRkj2OHMlXh9JAa9X3jv1Aw.jpeg?width=140&height=73&auto=webp&s=a4115a4e52c4efa603fbf9d1d3a71a8ea0236408",
"preview_image": "https://external-preview.redd.it/nv7JXdLRyJr_Uwgl5OCJQRkj2OHMlXh9JAa9X3jv1Aw.jpeg?auto=webp&s=8bada03e3caa2b10fec175d1896d12d8577977c5",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8v8r",
"fullname": "t3_1vh8v8r",
"scraped_at": "2026-08-18T09:36:55.069404+00:00",
"title": "reCAPTCHA v2 vs v3 vs Enterprise: What Actually Changes for Automation",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "The three flavors of reCAPTCHA look similar from the outside and behave completely differently once you are trying to automate through them. reCAPTCHA v2 gives you a checkbox and sometimes an image grid. reCAPTCHA v3 gives you nothing visible and quietly scores every request. Enterprise is either of those with a heavier backend and risk signals your token cannot see. If you treat them as one problem you will keep getting surprised. Here is what actually differs between v2, v3, and Enterprise from an automation standpoint, which fields matter for each, and how that changes the way you solve them. # reCAPTCHA v2: the interactive one v2 is the version most people picture. There is a widget with an "I am not a robot" checkbox. Click it and one of two things happens: it turns green and you get a token, or it expands into an image challenge where you select all the squares with buses, crosswalks, or fire hydrants. The public sitekey starts with `6L` and lives in the page as `data-sitekey`. The token field is `g-recaptcha-response`. When you submit it, the site's backend calls Google's `siteverify` and gets back a simple pass or fail plus the hostname. There is no score. v2 is binary: either the token is valid for that sitekey and origin, or it is not. For automation, v2 is the most straightforward to solve because the challenge is explicit. Either you complete the image grid or you mint the token in a trusted browser. The task type is `RecaptchaV2Task` and you send the `websiteURL` and `websiteKey`. That is essentially it. The [reCAPTCHA v2 solver](https://capzy.ai/solvers/recaptcha-v2) documents the exact payload. curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "RecaptchaV2Task", "websiteURL": "https://example.com/signup", "websiteKey": "6Lc_aQ0AAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } }' One wrinkle: v2 has an invisible variant that behaves more like v3, firing on a button click instead of showing a checkbox. Same field name, same token, but no visible UI until it decides to challenge you. # reCAPTCHA v3: the scoring one v3 removes the interaction entirely. There is no checkbox and no grid. The widget runs in the background on page load, watches how you behave, and produces a score between 0.0 and 1.0. A score of 1.0 means Google is confident you are human. 0.1 means it is confident you are not. The site decides what to do with that number. The critical difference for automation is that v3 never blocks you directly. It hands the site a score and an `action` name, and the site's own logic decides whether to let you log in, add friction, or reject you. So "solving" v3 is not about passing a challenge. It is about producing a token that carries a high enough score for the site's threshold, on the right action. That means two extra fields matter. The `action` (something like `login`, `submit`, or `homepage`) tags what the user is doing, and sites often check that it matches the page. The `minScore` you need depends entirely on the site's configuration, which you cannot see. A token scoring 0.7 sails through one site and gets rejected by another that demands 0.9. The task type is `RecaptchaV3Task` and you pass `pageAction` alongside the usual URL and key. Because the score depends on the browsing context, a v3 token minted from a fresh, trusted session with real navigation history scores much higher than one from a cold, obviously automated request. We cover the scoring mechanics in depth in a separate post; the short version is that entry into the site's real flow, the IP reputation, and the fingerprint all feed the number. >v3 does not have a "correct" answer. It has a score. Two identical requests from different IPs can get 0.9 and 0.3, and the only thing that changed was reputation. # reCAPTCHA Enterprise: same widgets, heavier backend Enterprise is where people get confused, because it does not have its own look. An Enterprise deployment renders as either a v2-style checkbox or a v3-style invisible score. The difference is on the backend. Google's Enterprise product gives the site richer risk analysis, an `assessment` API instead of plain `siteverify`, reason codes, and the ability to tune scoring per action with far more signals. For automation, the practical consequences are: * **The token format and field are the same** (`g-recaptcha-response`), so on the wire it looks like v2 or v3. * **The backend is stricter.** Enterprise can weigh more signals, including ones tied to the specific account or session, so a technically valid token can still be scored down. * **You may need to pass extra parameters** the site expects, and the sitekey is provisioned differently on Google's side. When solving Enterprise, you use the matching task type (the Enterprise variant of v2 or v3) and, for the invisible flavor, the same care about action names and session quality as regular v3, only more so. The [solver catalog](https://capzy.ai/solvers) lists the Enterprise task types separately from the standard ones for exactly this reason. # What this means for how you solve Line the three up and the pattern is clear: * **v2** is a pass/fail challenge. Mint a valid token for the sitekey and origin and you are done. Fingerprint and IP matter least here, though they still help avoid the harder image grids. * **v3** is a score. The token is easy to produce; getting a *high enough* score is the work. This is where clean egress and a coherent session pay off the most. * **Enterprise** is v2 or v3 with a stricter, signal-rich backend. Same fields, less margin for error. Because v3 and Enterprise grade the context and not just the challenge, a token by itself is often not enough. If your v3 scores are stuck around 0.3, the lever is rarely the solver. It is the reputation of the [IP you are coming from](https://capzy.ai/proxies) and how coherent your [browser fingerprint](https://capzy.ai/fingerprints) is. For flows that need a genuine engine with real navigation before the score is taken, drive them through a [Cloud Browser](https://capzy.ai/browser). # The bottom line v2 is a challenge you pass, v3 is a score you earn, and Enterprise is the same widgets watched more closely. Match your task type to the actual deployment, send the right `action` for v3, and remember that for anything score-based the token is the easy part. The reputation and coherence of the session behind it decide the number. Point your first request at the right task type from the [solver catalog](https://capzy.ai/solvers) with a [free Capzy account](https://capzy.ai/auth/register), and add the [Proxies API](https://capzy.ai/proxies) when your v3 scores need a lift.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>The three flavors of reCAPTCHA look similar from the outside and behave completely differently once you are trying to automate through them. reCAPTCHA v2 gives you a checkbox and sometimes an image grid. reCAPTCHA v3 gives you nothing visible and quietly scores every request. Enterprise is either of those with a heavier backend and risk signals your token cannot see. If you treat them as one problem you will keep getting surprised.</p> <p>Here is what actually differs between v2, v3, and Enterprise from an automation standpoint, which fields matter for each, and how that changes the way you solve them.</p> <h1>reCAPTCHA v2: the interactive one</h1> <p>v2 is the version most people picture. There is a widget with an "I am not a robot" checkbox. Click it and one of two things happens: it turns green and you get a token, or it expands into an image challenge where you select all the squares with buses, crosswalks, or fire hydrants. The public sitekey starts with <code>6L</code> and lives in the page as <code>data-sitekey</code>.</p> <p>The token field is <code>g-recaptcha-response</code>. When you submit it, the site's backend calls Google's <code>siteverify</code> and gets back a simple pass or fail plus the hostname. There is no score. v2 is binary: either the token is valid for that sitekey and origin, or it is not.</p> <p>For automation, v2 is the most straightforward to solve because the challenge is explicit. Either you complete the image grid or you mint the token in a trusted browser. The task type is <code>RecaptchaV2Task</code> and you send the <code>websiteURL</code> and <code>websiteKey</code>. That is essentially it. The <a href="https://capzy.ai/solvers/recaptcha-v2">reCAPTCHA v2 solver</a> documents the exact payload.</p> <pre><code>curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "RecaptchaV2Task", "websiteURL": "https://example.com/signup", "websiteKey": "6Lc_aQ0AAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } }' </code></pre> <p>One wrinkle: v2 has an invisible variant that behaves more like v3, firing on a button click instead of showing a checkbox. Same field name, same token, but no visible UI until it decides to challenge you.</p> <h1>reCAPTCHA v3: the scoring one</h1> <p>v3 removes the interaction entirely. There is no checkbox and no grid. The widget runs in the background on page load, watches how you behave, and produces a score between 0.0 and 1.0. A score of 1.0 means Google is confident you are human. 0.1 means it is confident you are not. The site decides what to do with that number.</p> <p>The critical difference for automation is that v3 never blocks you directly. It hands the site a score and an <code>action</code> name, and the site's own logic decides whether to let you log in, add friction, or reject you. So "solving" v3 is not about passing a challenge. It is about producing a token that carries a high enough score for the site's threshold, on the right action.</p> <p>That means two extra fields matter. The <code>action</code> (something like <code>login</code>, <code>submit</code>, or <code>homepage</code>) tags what the user is doing, and sites often check that it matches the page. The <code>minScore</code> you need depends entirely on the site's configuration, which you cannot see. A token scoring 0.7 sails through one site and gets rejected by another that demands 0.9.</p> <p>The task type is <code>RecaptchaV3Task</code> and you pass <code>pageAction</code> alongside the usual URL and key. Because the score depends on the browsing context, a v3 token minted from a fresh, trusted session with real navigation history scores much higher than one from a cold, obviously automated request. We cover the scoring mechanics in depth in a separate post; the short version is that entry into the site's real flow, the IP reputation, and the fingerprint all feed the number.</p> <blockquote> <p>v3 does not have a "correct" answer. It has a score. Two identical requests from different IPs can get 0.9 and 0.3, and the only thing that changed was reputation.</p> </blockquote> <h1>reCAPTCHA Enterprise: same widgets, heavier backend</h1> <p>Enterprise is where people get confused, because it does not have its own look. An Enterprise deployment renders as either a v2-style checkbox or a v3-style invisible score. The difference is on the backend. Google's Enterprise product gives the site richer risk analysis, an <code>assessment</code> API instead of plain <code>siteverify</code>, reason codes, and the ability to tune scoring per action with far more signals.</p> <p>For automation, the practical consequences are:</p> <ul> <li><strong>The token format and field are the same</strong> (<code>g-recaptcha-response</code>), so on the wire it looks like v2 or v3.</li> <li><strong>The backend is stricter.</strong> Enterprise can weigh more signals, including ones tied to the specific account or session, so a technically valid token can still be scored down.</li> <li><strong>You may need to pass extra parameters</strong> the site expects, and the sitekey is provisioned differently on Google's side.</li> </ul> <p>When solving Enterprise, you use the matching task type (the Enterprise variant of v2 or v3) and, for the invisible flavor, the same care about action names and session quality as regular v3, only more so. The <a href="https://capzy.ai/solvers">solver catalog</a> lists the Enterprise task types separately from the standard ones for exactly this reason.</p> <h1>What this means for how you solve</h1> <p>Line the three up and the pattern is clear:</p> <ul> <li><strong>v2</strong> is a pass/fail challenge. Mint a valid token for the sitekey and origin and you are done. Fingerprint and IP matter least here, though they still help avoid the harder image grids.</li> <li><strong>v3</strong> is a score. The token is easy to produce; getting a <em>high enough</em> score is the work. This is where clean egress and a coherent session pay off the most.</li> <li><strong>Enterprise</strong> is v2 or v3 with a stricter, signal-rich backend. Same fields, less margin for error.</li> </ul> <p>Because v3 and Enterprise grade the context and not just the challenge, a token by itself is often not enough. If your v3 scores are stuck around 0.3, the lever is rarely the solver. It is the reputation of the <a href="https://capzy.ai/proxies">IP you are coming from</a> and how coherent your <a href="https://capzy.ai/fingerprints">browser fingerprint</a> is. For flows that need a genuine engine with real navigation before the score is taken, drive them through a <a href="https://capzy.ai/browser">Cloud Browser</a>.</p> <h1>The bottom line</h1> <p>v2 is a challenge you pass, v3 is a score you earn, and Enterprise is the same widgets watched more closely. Match your task type to the actual deployment, send the right <code>action</code> for v3, and remember that for anything score-based the token is the easy part. The reputation and coherence of the session behind it decide the number.</p> <p>Point your first request at the right task type from the <a href="https://capzy.ai/solvers">solver catalog</a> with a <a href="https://capzy.ai/auth/register">free Capzy account</a>, and add the <a href="https://capzy.ai/proxies">Proxies API</a> when your v3 scores need a lift.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/recaptcha-v2-v3-enterprise-differences",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8v8r/recaptcha_v2_vs_v3_vs_enterprise_what_actually/",
"url_overridden_by_dest": "https://capzy.ai/blog/recaptcha-v2-v3-enterprise-differences",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:18:31+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/MAtAbrADxvbu4__G60hIzd6Rgiwq_Jg97tMVsHNE3UU.jpeg?width=140&height=73&auto=webp&s=390500b2467c3dce735734c9da44b2478be1792b",
"preview_image": "https://external-preview.redd.it/MAtAbrADxvbu4__G60hIzd6Rgiwq_Jg97tMVsHNE3UU.jpeg?auto=webp&s=529ccd86214246fef7a31ddf0ea0441e9267da82",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8uvu",
"fullname": "t3_1vh8uvu",
"scraped_at": "2026-08-18T09:36:55.069412+00:00",
"title": "How reCAPTCHA v3 Scoring Really Works (and Why Your Bot Scores 0.3)",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "reCAPTCHA v3 gives every request a score between 0.0 and 1.0, and if your automation keeps landing around 0.3, the token is almost never the problem. v3 is not a puzzle you solve. It is a reputation model that watches your IP, your browser, and how you got to the page, and then hands the site a number. Getting that number up means fixing the signals, not the solver. This post breaks down what feeds the v3 score, why bots cluster at the low end, and the specific levers that move the number. There is a worked example of the score changing based on nothing but egress. # The score is a probability, not a pass When v3 runs, it does not block anyone. It watches the session and produces `score`, a float from 0.0 to 1.0, plus the `action` name the developer assigned. Google frames it as the probability the interaction is legitimate. 0.9 means "very likely human." 0.1 means "very likely a bot." The site's own backend decides the threshold. A bank might reject anything under 0.7; a blog comment form might accept 0.3. Because the site controls the cutoff, there is no universal "passing" score. Your 0.5 token succeeds on one site and fails on the next. What you can control is pushing the number as high as possible so you clear more thresholds. And to do that you have to know what Google is looking at. # What actually feeds the score Google does not publish the model, but the inputs are well understood from behavior and documentation. Roughly in order of impact: * **IP reputation.** This is the heaviest single factor for most automation. Datacenter ranges that have hosted bot traffic score badly. Residential-quality IPs score well. The exact same request from two different IPs can score 0.9 and 0.3. * **Google cookies and account state.** A browser carrying a logged-in Google session, or a long-lived visitor cookie with real history, scores far higher than a cold, cookieless request. v3 leans hard on "have we seen this browser being human before." * **Browser fingerprint coherence.** Canvas, WebGL, `navigator` properties, client hints, and the user agent all need to agree. A headless browser that leaks `navigator.webdriver`, or a fingerprint where the UA says Chrome on Windows but the canvas says something else, gets marked down. * **Behavioral signals.** Mouse movement, timing, scroll, and how you arrived at the page. A request that appears from nowhere with no interaction looks worse than one that navigated in naturally. * **Flow entry.** This one surprises people. If you skip the site's real path to a page (deep-linking straight to a protected endpoint instead of clicking through the actual button that fires the challenge), the score suffers even if everything else is clean. # Why your bot scores 0.3 Stack those factors up and the typical bot fails several at once. It comes from a datacenter IP. It has no Google cookies. It runs a headless browser with tells. It has no mouse movement. It hit the endpoint directly. Each of those pushes the score down, and together they park you around 0.1 to 0.3. The instinct is to blame the solver and try a "better" one. But a solver that mints a v3 token still runs into the same reputation model. If the token is minted from the same bad IP and leaky browser, the score does not change. This is the core misunderstanding about v3: there is no token quality knob that overrides reputation. >A v3 token is trivial to obtain. A v3 token that scores 0.9 requires a session Google already trusts. Those are different problems, and only the second one matters. # A worked example Consider a login page whose backend rejects v3 scores under 0.7. You solve it three ways and check the score the site reports back: * Headless browser, datacenter IP, no cookies, direct navigation to `/login`. Score comes back around **0.3**. Rejected. * Same headless browser, but routed through a clean residential-quality IP. Score jumps to roughly **0.6**. Still short. * A real browser engine with a coherent fingerprint, carrying Google cookies from a warmed session, clicking through the homepage before reaching login on that same clean IP. Score lands around **0.9**. Accepted. Nothing about the "solving" changed between step 1 and step 3. What changed was the reputation of the session. That is the whole game. Here is how you would request a v3 token with the right action, which is the one solver-side detail that matters: import requests res = requests.post("https://api.capzy.ai/createTask", json={ "clientKey": "YOUR_API_KEY", "task": { "type": "RecaptchaV3Task", "websiteURL": "https://example.com/login", "websiteKey": "6Lc_aQ0AAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "pageAction": "login", "minScore": 0.7 } }).json() print(res["taskId"]) Sending the correct `pageAction` (here `login`) matters because sites often verify the action matches the page. A token minted for the wrong action can be discounted regardless of its raw score. # The levers that actually move the number If your v3 scores are stuck, work them in this order, because that is roughly their weight: * **Fix the IP first.** Move off datacenter ranges to residential-quality egress. This is usually the single biggest jump, and it is why the [Proxies API](https://capzy.ai/proxies) tends to matter more for v3 than the solver does. * **Make the fingerprint coherent.** No `webdriver` leaks, client hints that match the UA, a canvas and WebGL that agree with the platform. The [Fingerprint API](https://capzy.ai/fingerprints) exists to keep these signals consistent so nothing contradicts. * **Use a real engine and warm the session.** Drive the flow through a genuine [Cloud Browser](https://capzy.ai/browser), carry cookies, and enter through the site's real path rather than deep-linking. v3 rewards a browser it has seen behaving like a person. * **Send the right action** and solve on the actual page the challenge fires on. Capzy's v3 path is built around exactly this. The [reCAPTCHA solvers in the catalog](https://capzy.ai/solvers) mint tokens from trusted, warmed sessions rather than cold headless requests, which is why score is the metric they optimize for, not just token validity. # The takeaway v3 scoring is a reputation model, not a challenge. Your bot scores 0.3 because its IP, cookies, fingerprint, and flow all read as automated, and no solver overrides that. Fix the egress, make the fingerprint coherent, warm the session in a real browser, and send the correct action. The score follows. Get a v3 token that actually scores well with a [free Capzy account](https://capzy.ai/auth/register), and back it with the [Proxies API](https://capzy.ai/proxies) and [Fingerprint API](https://capzy.ai/fingerprints) so the session behind your token is one Google trusts.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>reCAPTCHA v3 gives every request a score between 0.0 and 1.0, and if your automation keeps landing around 0.3, the token is almost never the problem. v3 is not a puzzle you solve. It is a reputation model that watches your IP, your browser, and how you got to the page, and then hands the site a number. Getting that number up means fixing the signals, not the solver.</p> <p>This post breaks down what feeds the v3 score, why bots cluster at the low end, and the specific levers that move the number. There is a worked example of the score changing based on nothing but egress.</p> <h1>The score is a probability, not a pass</h1> <p>When v3 runs, it does not block anyone. It watches the session and produces <code>score</code>, a float from 0.0 to 1.0, plus the <code>action</code> name the developer assigned. Google frames it as the probability the interaction is legitimate. 0.9 means "very likely human." 0.1 means "very likely a bot." The site's own backend decides the threshold. A bank might reject anything under 0.7; a blog comment form might accept 0.3.</p> <p>Because the site controls the cutoff, there is no universal "passing" score. Your 0.5 token succeeds on one site and fails on the next. What you can control is pushing the number as high as possible so you clear more thresholds. And to do that you have to know what Google is looking at.</p> <h1>What actually feeds the score</h1> <p>Google does not publish the model, but the inputs are well understood from behavior and documentation. Roughly in order of impact:</p> <ul> <li><strong>IP reputation.</strong> This is the heaviest single factor for most automation. Datacenter ranges that have hosted bot traffic score badly. Residential-quality IPs score well. The exact same request from two different IPs can score 0.9 and 0.3.</li> <li><strong>Google cookies and account state.</strong> A browser carrying a logged-in Google session, or a long-lived visitor cookie with real history, scores far higher than a cold, cookieless request. v3 leans hard on "have we seen this browser being human before."</li> <li><strong>Browser fingerprint coherence.</strong> Canvas, WebGL, <code>navigator</code> properties, client hints, and the user agent all need to agree. A headless browser that leaks <code>navigator.webdriver</code>, or a fingerprint where the UA says Chrome on Windows but the canvas says something else, gets marked down.</li> <li><strong>Behavioral signals.</strong> Mouse movement, timing, scroll, and how you arrived at the page. A request that appears from nowhere with no interaction looks worse than one that navigated in naturally.</li> <li><strong>Flow entry.</strong> This one surprises people. If you skip the site's real path to a page (deep-linking straight to a protected endpoint instead of clicking through the actual button that fires the challenge), the score suffers even if everything else is clean.</li> </ul> <h1>Why your bot scores 0.3</h1> <p>Stack those factors up and the typical bot fails several at once. It comes from a datacenter IP. It has no Google cookies. It runs a headless browser with tells. It has no mouse movement. It hit the endpoint directly. Each of those pushes the score down, and together they park you around 0.1 to 0.3.</p> <p>The instinct is to blame the solver and try a "better" one. But a solver that mints a v3 token still runs into the same reputation model. If the token is minted from the same bad IP and leaky browser, the score does not change. This is the core misunderstanding about v3: there is no token quality knob that overrides reputation.</p> <blockquote> <p>A v3 token is trivial to obtain. A v3 token that scores 0.9 requires a session Google already trusts. Those are different problems, and only the second one matters.</p> </blockquote> <h1>A worked example</h1> <p>Consider a login page whose backend rejects v3 scores under 0.7. You solve it three ways and check the score the site reports back:</p> <ul> <li>Headless browser, datacenter IP, no cookies, direct navigation to <code>/login</code>. Score comes back around <strong>0.3</strong>. Rejected.</li> <li>Same headless browser, but routed through a clean residential-quality IP. Score jumps to roughly <strong>0.6</strong>. Still short.</li> <li>A real browser engine with a coherent fingerprint, carrying Google cookies from a warmed session, clicking through the homepage before reaching login on that same clean IP. Score lands around <strong>0.9</strong>. Accepted.</li> </ul> <p>Nothing about the "solving" changed between step 1 and step 3. What changed was the reputation of the session. That is the whole game.</p> <p>Here is how you would request a v3 token with the right action, which is the one solver-side detail that matters:</p> <pre><code>import requests res = requests.post("https://api.capzy.ai/createTask", json={ "clientKey": "YOUR_API_KEY", "task": { "type": "RecaptchaV3Task", "websiteURL": "https://example.com/login", "websiteKey": "6Lc_aQ0AAAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "pageAction": "login", "minScore": 0.7 } }).json() print(res["taskId"]) </code></pre> <p>Sending the correct <code>pageAction</code> (here <code>login</code>) matters because sites often verify the action matches the page. A token minted for the wrong action can be discounted regardless of its raw score.</p> <h1>The levers that actually move the number</h1> <p>If your v3 scores are stuck, work them in this order, because that is roughly their weight:</p> <ul> <li><strong>Fix the IP first.</strong> Move off datacenter ranges to residential-quality egress. This is usually the single biggest jump, and it is why the <a href="https://capzy.ai/proxies">Proxies API</a> tends to matter more for v3 than the solver does.</li> <li><strong>Make the fingerprint coherent.</strong> No <code>webdriver</code> leaks, client hints that match the UA, a canvas and WebGL that agree with the platform. The <a href="https://capzy.ai/fingerprints">Fingerprint API</a> exists to keep these signals consistent so nothing contradicts.</li> <li><strong>Use a real engine and warm the session.</strong> Drive the flow through a genuine <a href="https://capzy.ai/browser">Cloud Browser</a>, carry cookies, and enter through the site's real path rather than deep-linking. v3 rewards a browser it has seen behaving like a person.</li> <li><strong>Send the right action</strong> and solve on the actual page the challenge fires on.</li> </ul> <p>Capzy's v3 path is built around exactly this. The <a href="https://capzy.ai/solvers">reCAPTCHA solvers in the catalog</a> mint tokens from trusted, warmed sessions rather than cold headless requests, which is why score is the metric they optimize for, not just token validity.</p> <h1>The takeaway</h1> <p>v3 scoring is a reputation model, not a challenge. Your bot scores 0.3 because its IP, cookies, fingerprint, and flow all read as automated, and no solver overrides that. Fix the egress, make the fingerprint coherent, warm the session in a real browser, and send the correct action. The score follows.</p> <p>Get a v3 token that actually scores well with a <a href="https://capzy.ai/auth/register">free Capzy account</a>, and back it with the <a href="https://capzy.ai/proxies">Proxies API</a> and <a href="https://capzy.ai/fingerprints">Fingerprint API</a> so the session behind your token is one Google trusts.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/how-recaptcha-v3-scoring-works",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8uvu/how_recaptcha_v3_scoring_really_works_and_why/",
"url_overridden_by_dest": "https://capzy.ai/blog/how-recaptcha-v3-scoring-works",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:18:10+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/VZl0kiwhdYOTgs4BJHmgByMYC4EsGt4ThZl9fIWpx9g.jpeg?width=140&height=73&auto=webp&s=88cd5a8c378b776b03773e843fb3e5b6fca3895f",
"preview_image": "https://external-preview.redd.it/VZl0kiwhdYOTgs4BJHmgByMYC4EsGt4ThZl9fIWpx9g.jpeg?auto=webp&s=77a1a8b962f744e9e22ec60da0ba51bf62f81923",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8ug2",
"fullname": "t3_1vh8ug2",
"scraped_at": "2026-08-18T09:36:55.069419+00:00",
"title": "Getting Past the Cloudflare 'Checking Your Browser' Interstitial",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "The Cloudflare "Checking your browser before you access..." page is not a captcha, even though it looks like one. It is an interstitial that runs a JavaScript challenge, verifies your browser is real, and then issues a `cf_clearance` cookie that lets you through for a while. If your scraper keeps landing on that page and never getting past it, the fix is not solving a puzzle. It is producing a browser environment Cloudflare trusts and holding onto the cookie it hands back. This post explains what the interstitial actually checks, why automation gets stuck in a loop on it, and the practical ways to get the `cf_clearance` cookie and keep it. # What the interstitial is doing When Cloudflare's bot management decides a request needs verification, it serves the challenge page instead of the real content. That page runs a JavaScript challenge in your browser. The challenge does a proof-of-work computation and, more importantly, probes your environment: it checks JavaScript execution, timing, TLS characteristics, and a range of browser properties to decide whether a genuine engine is running the code. If your browser passes, Cloudflare sets a cookie named `cf_clearance` scoped to the domain. Every subsequent request that carries that cookie skips the interstitial until it expires or gets invalidated. So the whole objective is narrow: run the challenge in a browser Cloudflare accepts, capture `cf_clearance`, and send it on the requests that follow. There are a few distinct things people lump together here. The old "Checking your browser" interstitial (sometimes called the JS challenge or "I am under attack mode"), the newer managed challenge that may embed a Turnstile widget, and hard blocks. The clearance-cookie flow applies to the challenge interstitials. A hard 1020 block is a different problem and usually means the IP or request pattern is banned outright, not challenged. # Why automation gets stuck in a loop The classic symptom is a scraper that fetches the interstitial, submits, gets the interstitial again, and never reaches the content. That loop happens for a handful of reasons: * **The challenge JavaScript never runs.** A plain HTTP client (requests, curl, a raw fetch) does not execute JavaScript, so the challenge cannot complete. You get served the page forever. This is the single most common cause. * **The browser leaks automation.** A headless browser that exposes `navigator.webdriver`, has an inconsistent user agent, or fails environment checks completes the challenge but Cloudflare declines to trust it, so it re-challenges. * **The clearance cookie is not being reused.** `cf_clearance` is bound to the IP and the browser fingerprint that earned it. If you solve in one context and then make requests from a different IP or a different fingerprint, the cookie is rejected and you loop again. * **The IP has a bad reputation.** From a flagged datacenter range, Cloudflare may keep challenging regardless, or hand you the harder managed challenge every time. >`cf_clearance` is not a free-floating pass. It is bound to the IP address and the browser fingerprint that earned it. Move either one and the cookie stops working. That binding is the detail most people miss. You cannot solve the interstitial in a real browser on your laptop, copy the `cf_clearance` cookie into your datacenter scraper, and expect it to work. Cloudflare ties the cookie to the session that produced it. # How to get past it There are two workable approaches, depending on how much of the page you need. # 1. Run a real browser and carry the cookie forward The most reliable path is to complete the challenge in an actual browser engine, capture `cf_clearance`, and reuse it, from the same IP and fingerprint, for the rest of your requests. This is exactly what a [Cloud Browser](https://capzy.ai/browser) is for. You drive a genuine Chrome engine that executes the challenge JavaScript, passes the environment checks, and gets the cookie. Then you either keep working inside that browser or extract the cookie and continue with an HTTP client that mirrors the same session. The rules for keeping the cookie alive: * Make every follow-up request from the **same egress IP** the cookie was earned on. * Match the **user agent and fingerprint** to the browser that solved the challenge. A mismatch invalidates the cookie. * Respect the cookie's lifetime and re-solve when it expires rather than hammering with a stale one. # 2. Solve the embedded challenge as a token Newer Cloudflare deployments render the interstitial as a managed challenge that includes a Turnstile widget. In that case the thing you need is a `cf-turnstile-response` token, which you can obtain from a solving API and submit to clear the page. The [Turnstile solver](https://capzy.ai/solvers/turnstile) handles this, and the wider [solver catalog](https://capzy.ai/solvers) covers the Cloudflare challenge variants you might encounter on the same domain. Here is a minimal solve for the Turnstile-backed variant: curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "TurnstileTask", "websiteURL": "https://example.com/", "websiteKey": "0x4AAAAAAABkMYinukE8nzY" } }' # poll getTaskResult, then submit the returned token to clear the challenge # The IP problem underneath it all Whichever path you take, egress reputation sits underneath everything. Cloudflare is dramatically more willing to issue and honor `cf_clearance` for residential-quality IPs than for datacenter ranges it associates with automation. If you are stuck in a challenge loop even with a real browser, the IP is the first thing to change. Routing through the [Proxies API](https://capzy.ai/proxies) so your requests come from clean egress frequently turns a permanent loop into a clean pass. And because the cookie is fingerprint-bound, coherence matters just as much. If your user agent, client hints, and TLS fingerprint contradict each other, Cloudflare re-challenges even after a technically successful solve. The [Fingerprint API](https://capzy.ai/fingerprints) keeps those signals consistent so the browser that earns `cf_clearance` and the client that reuses it look like the same visitor. A working setup usually combines all three: a real browser to run the challenge, a clean IP to earn trust, and a coherent fingerprint to keep the cookie valid. Miss any one and you are back in the loop. # Summary The "Checking your browser" page is a JavaScript challenge that mints a `cf_clearance` cookie for browsers Cloudflare trusts. You get stuck because a raw HTTP client cannot run the challenge, a leaky browser is not trusted, or the cookie is being reused from the wrong IP or fingerprint. Solve it in a real engine, from a clean IP, with a coherent fingerprint, and carry the cookie forward unchanged. Clear the interstitial reliably with a [free Capzy account](https://capzy.ai/auth/register): run the challenge in a [Cloud Browser](https://capzy.ai/browser), route through the [Proxies API](https://capzy.ai/proxies), and let the [solver catalog](https://capzy.ai/solvers) handle any Turnstile-backed variant.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>The Cloudflare "Checking your browser before you access..." page is not a captcha, even though it looks like one. It is an interstitial that runs a JavaScript challenge, verifies your browser is real, and then issues a <code>cf_clearance</code> cookie that lets you through for a while. If your scraper keeps landing on that page and never getting past it, the fix is not solving a puzzle. It is producing a browser environment Cloudflare trusts and holding onto the cookie it hands back.</p> <p>This post explains what the interstitial actually checks, why automation gets stuck in a loop on it, and the practical ways to get the <code>cf_clearance</code> cookie and keep it.</p> <h1>What the interstitial is doing</h1> <p>When Cloudflare's bot management decides a request needs verification, it serves the challenge page instead of the real content. That page runs a JavaScript challenge in your browser. The challenge does a proof-of-work computation and, more importantly, probes your environment: it checks JavaScript execution, timing, TLS characteristics, and a range of browser properties to decide whether a genuine engine is running the code.</p> <p>If your browser passes, Cloudflare sets a cookie named <code>cf_clearance</code> scoped to the domain. Every subsequent request that carries that cookie skips the interstitial until it expires or gets invalidated. So the whole objective is narrow: run the challenge in a browser Cloudflare accepts, capture <code>cf_clearance</code>, and send it on the requests that follow.</p> <p>There are a few distinct things people lump together here. The old "Checking your browser" interstitial (sometimes called the JS challenge or "I am under attack mode"), the newer managed challenge that may embed a Turnstile widget, and hard blocks. The clearance-cookie flow applies to the challenge interstitials. A hard 1020 block is a different problem and usually means the IP or request pattern is banned outright, not challenged.</p> <h1>Why automation gets stuck in a loop</h1> <p>The classic symptom is a scraper that fetches the interstitial, submits, gets the interstitial again, and never reaches the content. That loop happens for a handful of reasons:</p> <ul> <li><strong>The challenge JavaScript never runs.</strong> A plain HTTP client (requests, curl, a raw fetch) does not execute JavaScript, so the challenge cannot complete. You get served the page forever. This is the single most common cause.</li> <li><strong>The browser leaks automation.</strong> A headless browser that exposes <code>navigator.webdriver</code>, has an inconsistent user agent, or fails environment checks completes the challenge but Cloudflare declines to trust it, so it re-challenges.</li> <li><strong>The clearance cookie is not being reused.</strong> <code>cf_clearance</code> is bound to the IP and the browser fingerprint that earned it. If you solve in one context and then make requests from a different IP or a different fingerprint, the cookie is rejected and you loop again.</li> <li><strong>The IP has a bad reputation.</strong> From a flagged datacenter range, Cloudflare may keep challenging regardless, or hand you the harder managed challenge every time.</li> </ul> <blockquote> <p><code>cf_clearance</code> is not a free-floating pass. It is bound to the IP address and the browser fingerprint that earned it. Move either one and the cookie stops working.</p> </blockquote> <p>That binding is the detail most people miss. You cannot solve the interstitial in a real browser on your laptop, copy the <code>cf_clearance</code> cookie into your datacenter scraper, and expect it to work. Cloudflare ties the cookie to the session that produced it.</p> <h1>How to get past it</h1> <p>There are two workable approaches, depending on how much of the page you need.</p> <h1>1. Run a real browser and carry the cookie forward</h1> <p>The most reliable path is to complete the challenge in an actual browser engine, capture <code>cf_clearance</code>, and reuse it, from the same IP and fingerprint, for the rest of your requests. This is exactly what a <a href="https://capzy.ai/browser">Cloud Browser</a> is for. You drive a genuine Chrome engine that executes the challenge JavaScript, passes the environment checks, and gets the cookie. Then you either keep working inside that browser or extract the cookie and continue with an HTTP client that mirrors the same session.</p> <p>The rules for keeping the cookie alive:</p> <ul> <li>Make every follow-up request from the <strong>same egress IP</strong> the cookie was earned on.</li> <li>Match the <strong>user agent and fingerprint</strong> to the browser that solved the challenge. A mismatch invalidates the cookie.</li> <li>Respect the cookie's lifetime and re-solve when it expires rather than hammering with a stale one.</li> </ul> <h1>2. Solve the embedded challenge as a token</h1> <p>Newer Cloudflare deployments render the interstitial as a managed challenge that includes a Turnstile widget. In that case the thing you need is a <code>cf-turnstile-response</code> token, which you can obtain from a solving API and submit to clear the page. The <a href="https://capzy.ai/solvers/turnstile">Turnstile solver</a> handles this, and the wider <a href="https://capzy.ai/solvers">solver catalog</a> covers the Cloudflare challenge variants you might encounter on the same domain.</p> <p>Here is a minimal solve for the Turnstile-backed variant:</p> <pre><code>curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_API_KEY", "task": { "type": "TurnstileTask", "websiteURL": "https://example.com/", "websiteKey": "0x4AAAAAAABkMYinukE8nzY" } }' # poll getTaskResult, then submit the returned token to clear the challenge </code></pre> <h1>The IP problem underneath it all</h1> <p>Whichever path you take, egress reputation sits underneath everything. Cloudflare is dramatically more willing to issue and honor <code>cf_clearance</code> for residential-quality IPs than for datacenter ranges it associates with automation. If you are stuck in a challenge loop even with a real browser, the IP is the first thing to change. Routing through the <a href="https://capzy.ai/proxies">Proxies API</a> so your requests come from clean egress frequently turns a permanent loop into a clean pass.</p> <p>And because the cookie is fingerprint-bound, coherence matters just as much. If your user agent, client hints, and TLS fingerprint contradict each other, Cloudflare re-challenges even after a technically successful solve. The <a href="https://capzy.ai/fingerprints">Fingerprint API</a> keeps those signals consistent so the browser that earns <code>cf_clearance</code> and the client that reuses it look like the same visitor.</p> <p>A working setup usually combines all three: a real browser to run the challenge, a clean IP to earn trust, and a coherent fingerprint to keep the cookie valid. Miss any one and you are back in the loop.</p> <h1>Summary</h1> <p>The "Checking your browser" page is a JavaScript challenge that mints a <code>cf_clearance</code> cookie for browsers Cloudflare trusts. You get stuck because a raw HTTP client cannot run the challenge, a leaky browser is not trusted, or the cookie is being reused from the wrong IP or fingerprint. Solve it in a real engine, from a clean IP, with a coherent fingerprint, and carry the cookie forward unchanged.</p> <p>Clear the interstitial reliably with a <a href="https://capzy.ai/auth/register">free Capzy account</a>: run the challenge in a <a href="https://capzy.ai/browser">Cloud Browser</a>, route through the <a href="https://capzy.ai/proxies">Proxies API</a>, and let the <a href="https://capzy.ai/solvers">solver catalog</a> handle any Turnstile-backed variant.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/cloudflare-checking-your-browser-cf-clearance",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8ug2/getting_past_the_cloudflare_checking_your_browser/",
"url_overridden_by_dest": "https://capzy.ai/blog/cloudflare-checking-your-browser-cf-clearance",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:17:41+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/YlZo5IThbG0s2RnO36oJU_aBDiRlP3C52EXJlZpdJv0.jpeg?width=140&height=73&auto=webp&s=40ccbbc7a2b6e5d9f1199499ed2b28d703387275",
"preview_image": "https://external-preview.redd.it/YlZo5IThbG0s2RnO36oJU_aBDiRlP3C52EXJlZpdJv0.jpeg?auto=webp&s=4842bb069defbc5e87f4eda2785712e4c9c00156",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8u1b",
"fullname": "t3_1vh8u1b",
"scraped_at": "2026-08-18T09:36:55.069427+00:00",
"title": "Turnstile vs reCAPTCHA vs hCaptcha: A Practical Comparison",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "If you automate against real sites, you will meet three CAPTCHA families more than any others: Cloudflare Turnstile, Google reCAPTCHA, and hCaptcha. They look similar from the outside, a widget that gates a form, but they differ in the token field they write, how much the user has to do, and how they behave under automation. Knowing those differences up front saves you from wiring the wrong task type and wondering why the token bounces. This post is a factual side-by-side. It covers the token field name each one uses, how interactive each is, where each sits on privacy, and which Capzy Solver task type maps to each. The short version: Turnstile writes `cf-turnstile-response` and is usually invisible or low-friction, reCAPTCHA writes `g-recaptcha-response` and comes in a checkbox/grid v2 and a scored v3, and hCaptcha writes `h-captcha-response` and leans on image grids. All three are solvable through Capzy [CAPTCHA solving](https://capzy.ai/solvers); you just have to pick the matching task type. # Token field names The single most practical thing to know is where each widget writes its answer, because that is the hidden field you read or set when you inject a solved token. * Cloudflare Turnstile writes to a hidden input named `cf-turnstile-response`. * Google reCAPTCHA (both v2 and v3) writes to `g-recaptcha-response`. * hCaptcha writes to `h-captcha-response`. These names are stable and site-independent, which is why token injection code can be written once per family. When the Solver API returns a token, that string is exactly what belongs in the corresponding field before the form is submitted. # Interactivity: how much the user does The three differ sharply in how much friction they put in front of a real visitor, and that shapes how they behave when a bot shows up. Cloudflare Turnstile is mostly invisible or "managed." In the common case the visitor sees a brief spinner and nothing else; the widget makes its decision from passive signals and rarely asks for a click. That low friction is the selling point. reCAPTCHA splits into two very different products. v2 is the familiar "I'm not a robot" checkbox that can escalate to an image grid (pick the crosswalks, the buses, and so on). v3 is invisible and returns a score from 0.0 to 1.0 that the site uses however it likes; there is no checkbox and no grid, just a token the backend grades. hCaptcha is typically an image grid challenge, visually similar to reCAPTCHA v2's grid but run by a different vendor with its own puzzle set. It tends to ask the visitor to classify images more often than Turnstile asks for anything. >Interactivity is not the same as difficulty to automate. An invisible widget can be harder to pass than a visible grid, because "invisible" means the decision rests entirely on signals you do not directly see. # Privacy stance The three vendors market different privacy positions, and it is worth being accurate rather than promotional about them. Cloudflare positions Turnstile as a privacy-preserving alternative that avoids the tracking associated with older CAPTCHA products, and it does not require the visitor to label images. hCaptcha markets itself around data privacy and gives site operators control, historically tied to a model where labeling work had value. reCAPTCHA is a Google product and its signals live in Google's ecosystem, which is precisely the concern the other two position against. None of this changes how you integrate a solver, but it explains why you see all three in the wild instead of one winner. # Relative automation difficulty Being balanced here matters, because difficulty depends on the site's configuration, not just the vendor. A token from a solver is necessary but not sufficient for any of the three. The token proves the challenge was answered; it does not fix a flagged IP or a contradictory client fingerprint. reCAPTCHA v3 makes this obvious: it returns a score, and a site can reject a technically valid token because the surrounding session looked wrong. Turnstile's invisible mode leans on passive signals, so a clean egress and a coherent client help more than they do on a pure grid puzzle. hCaptcha's grids are more about the visual solve, but IP reputation still colors how often and how hard it challenges you. That is why reliable solving on any of the three usually pairs the Solver API with a clean IP from the [Proxies API](https://capzy.ai/proxies) and a coherent identity from the [Fingerprint API](https://capzy.ai/fingerprints), and sometimes a real engine like the [Cloud Browser](https://capzy.ai/browser) or [Capium](https://capzy.ai/capium) when the site mints tokens client-side. # Which Solver task type to use Here is the direct mapping. Pick the task type that matches the widget on the page: * Cloudflare Turnstile -> `TurnstileTask` * reCAPTCHA v2 (checkbox / image grid) -> `RecaptchaV2Task` * reCAPTCHA v3 (invisible score) -> `RecaptchaV3Task` * hCaptcha (image grid) -> `HCaptchaTask` A minimal Turnstile solve looks like this. You create a task, then poll for the result: # 1) create the task curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "TurnstileTask", "websiteURL": "https://example.com/login", "websiteKey": "0x4AAAAAAA..." } }' # -> { "errorId": 0, "taskId": "b9f2..." } # 2) fetch the result until status is ready curl -s https://api.capzy.ai/getTaskResult \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "taskId": "b9f2..." }' # -> { "status": "ready", "solution": { "token": "0.AB..." } } Swap `TurnstileTask` for `RecaptchaV2Task`, `RecaptchaV3Task`, or `HCaptchaTask` and the shape is the same; the fields you supply and the solution you get back are what differ. The full list of supported types lives at [capzy.ai/solvers](https://capzy.ai/solvers). # Closing There is no single "best" of the three from an automation seat. Turnstile is the low-friction invisible one, reCAPTCHA is the two-headed checkbox/score product, and hCaptcha is the image-grid alternative. What matters in practice is reading the widget correctly, picking the matching task type, dropping the token into the right field, and remembering that the token is only one of four things a strict site checks. Ready to solve all three behind one key? [Create a free account](https://capzy.ai/auth/register) and run a `TurnstileTask` against your own target to see the flow end to end.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>If you automate against real sites, you will meet three CAPTCHA families more than any others: Cloudflare Turnstile, Google reCAPTCHA, and hCaptcha. They look similar from the outside, a widget that gates a form, but they differ in the token field they write, how much the user has to do, and how they behave under automation. Knowing those differences up front saves you from wiring the wrong task type and wondering why the token bounces.</p> <p>This post is a factual side-by-side. It covers the token field name each one uses, how interactive each is, where each sits on privacy, and which Capzy Solver task type maps to each. The short version: Turnstile writes <code>cf-turnstile-response</code> and is usually invisible or low-friction, reCAPTCHA writes <code>g-recaptcha-response</code> and comes in a checkbox/grid v2 and a scored v3, and hCaptcha writes <code>h-captcha-response</code> and leans on image grids. All three are solvable through Capzy <a href="https://capzy.ai/solvers">CAPTCHA solving</a>; you just have to pick the matching task type.</p> <h1>Token field names</h1> <p>The single most practical thing to know is where each widget writes its answer, because that is the hidden field you read or set when you inject a solved token.</p> <ul> <li>Cloudflare Turnstile writes to a hidden input named <code>cf-turnstile-response</code>.</li> <li>Google reCAPTCHA (both v2 and v3) writes to <code>g-recaptcha-response</code>.</li> <li>hCaptcha writes to <code>h-captcha-response</code>.</li> </ul> <p>These names are stable and site-independent, which is why token injection code can be written once per family. When the Solver API returns a token, that string is exactly what belongs in the corresponding field before the form is submitted.</p> <h1>Interactivity: how much the user does</h1> <p>The three differ sharply in how much friction they put in front of a real visitor, and that shapes how they behave when a bot shows up.</p> <p>Cloudflare Turnstile is mostly invisible or "managed." In the common case the visitor sees a brief spinner and nothing else; the widget makes its decision from passive signals and rarely asks for a click. That low friction is the selling point.</p> <p>reCAPTCHA splits into two very different products. v2 is the familiar "I'm not a robot" checkbox that can escalate to an image grid (pick the crosswalks, the buses, and so on). v3 is invisible and returns a score from 0.0 to 1.0 that the site uses however it likes; there is no checkbox and no grid, just a token the backend grades.</p> <p>hCaptcha is typically an image grid challenge, visually similar to reCAPTCHA v2's grid but run by a different vendor with its own puzzle set. It tends to ask the visitor to classify images more often than Turnstile asks for anything.</p> <blockquote> <p>Interactivity is not the same as difficulty to automate. An invisible widget can be harder to pass than a visible grid, because "invisible" means the decision rests entirely on signals you do not directly see.</p> </blockquote> <h1>Privacy stance</h1> <p>The three vendors market different privacy positions, and it is worth being accurate rather than promotional about them.</p> <p>Cloudflare positions Turnstile as a privacy-preserving alternative that avoids the tracking associated with older CAPTCHA products, and it does not require the visitor to label images. hCaptcha markets itself around data privacy and gives site operators control, historically tied to a model where labeling work had value. reCAPTCHA is a Google product and its signals live in Google's ecosystem, which is precisely the concern the other two position against. None of this changes how you integrate a solver, but it explains why you see all three in the wild instead of one winner.</p> <h1>Relative automation difficulty</h1> <p>Being balanced here matters, because difficulty depends on the site's configuration, not just the vendor.</p> <p>A token from a solver is necessary but not sufficient for any of the three. The token proves the challenge was answered; it does not fix a flagged IP or a contradictory client fingerprint. reCAPTCHA v3 makes this obvious: it returns a score, and a site can reject a technically valid token because the surrounding session looked wrong. Turnstile's invisible mode leans on passive signals, so a clean egress and a coherent client help more than they do on a pure grid puzzle. hCaptcha's grids are more about the visual solve, but IP reputation still colors how often and how hard it challenges you.</p> <p>That is why reliable solving on any of the three usually pairs the Solver API with a clean IP from the <a href="https://capzy.ai/proxies">Proxies API</a> and a coherent identity from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a>, and sometimes a real engine like the <a href="https://capzy.ai/browser">Cloud Browser</a> or <a href="https://capzy.ai/capium">Capium</a> when the site mints tokens client-side.</p> <h1>Which Solver task type to use</h1> <p>Here is the direct mapping. Pick the task type that matches the widget on the page:</p> <ul> <li>Cloudflare Turnstile -> <code>TurnstileTask</code></li> <li>reCAPTCHA v2 (checkbox / image grid) -> <code>RecaptchaV2Task</code></li> <li>reCAPTCHA v3 (invisible score) -> <code>RecaptchaV3Task</code></li> <li>hCaptcha (image grid) -> <code>HCaptchaTask</code></li> </ul> <p>A minimal Turnstile solve looks like this. You create a task, then poll for the result:</p> <pre><code># 1) create the task curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "TurnstileTask", "websiteURL": "https://example.com/login", "websiteKey": "0x4AAAAAAA..." } }' # -> { "errorId": 0, "taskId": "b9f2..." } # 2) fetch the result until status is ready curl -s https://api.capzy.ai/getTaskResult \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "taskId": "b9f2..." }' # -> { "status": "ready", "solution": { "token": "0.AB..." } } </code></pre> <p>Swap <code>TurnstileTask</code> for <code>RecaptchaV2Task</code>, <code>RecaptchaV3Task</code>, or <code>HCaptchaTask</code> and the shape is the same; the fields you supply and the solution you get back are what differ. The full list of supported types lives at <a href="https://capzy.ai/solvers">capzy.ai/solvers</a>.</p> <h1>Closing</h1> <p>There is no single "best" of the three from an automation seat. Turnstile is the low-friction invisible one, reCAPTCHA is the two-headed checkbox/score product, and hCaptcha is the image-grid alternative. What matters in practice is reading the widget correctly, picking the matching task type, dropping the token into the right field, and remembering that the token is only one of four things a strict site checks.</p> <p>Ready to solve all three behind one key? <a href="https://capzy.ai/auth/register">Create a free account</a> and run a <code>TurnstileTask</code> against your own target to see the flow end to end.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/turnstile-vs-recaptcha-vs-hcaptcha",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8u1b/turnstile_vs_recaptcha_vs_hcaptcha_a_practical/",
"url_overridden_by_dest": "https://capzy.ai/blog/turnstile-vs-recaptcha-vs-hcaptcha",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:17:17+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/4pJtBi3SFiJ4r2fPYn1scIjaolJoe-QRWQDbWqCj7j8.jpeg?width=140&height=73&auto=webp&s=830eb6421113d88fd915384e95ea43cc48f8a740",
"preview_image": "https://external-preview.redd.it/4pJtBi3SFiJ4r2fPYn1scIjaolJoe-QRWQDbWqCj7j8.jpeg?auto=webp&s=86f9dd37c0f545bbf334697c939e6d71731932f4",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh8ts0",
"fullname": "t3_1vh8ts0",
"scraped_at": "2026-08-18T09:36:55.069434+00:00",
"title": "DataDome, PerimeterX, and Kasada: How Modern Bot Managers Fingerprint You",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "# DataDome, PerimeterX, and Kasada: How Modern Bot Managers Fingerprint You If your requests are getting a 403 with a `datadome` cookie stapled to the response, or a challenge page from PerimeterX, or a hard block that keeps demanding an `x-kpsdk-ct` header, you are dealing with the three bot managers that gate a large slice of the modern web. DataDome, PerimeterX (now part of HUMAN), and Kasada fingerprinting all work on the same principle: collect hundreds of signals from your browser and network, score them, and decide whether you look like a person or a program. They just disagree on which signals they trust most. This post walks through what each one actually measures, the field names you will see in traffic, and why the same script passes on one setup and dies on another. # What "fingerprinting" means here Fingerprinting is not one number. It is a bundle of observations collected across three layers: * **Network layer**: your IP address and its reputation, the TLS handshake (JA3/JA4), the HTTP/2 frame ordering. * **Client layer**: JavaScript that reads your `navigator` object, canvas and WebGL rendering, screen metrics, timezone, installed fonts, audio stack quirks. * **Behavioral layer**: mouse paths, scroll cadence, key timing, and how the page was reached. A bot manager takes all of that, hashes and weights it, and compares it against what a real Chrome on real hardware behind a residential IP produces. Deviation costs you points. Enough points and you get a challenge or a block. >The mistake most scrapers make is treating detection as a single wall. It is a scoring engine. You do not need a perfect fingerprint, you need to stay under the threshold on every layer at once. # DataDome DataDome leans hard on network reputation and a fast client-side JS payload. The tell is the `datadome` cookie. On a clean pass it gets set and reused. When you trip the scorer, you get a 403 and a challenge (often a slider) served from a [`geo.captcha-delivery.com`](http://geo.captcha-delivery.com) style endpoint, and the response carries a fresh `datadome` value plus a `dd` JSON blob describing the challenge. What it scores heavily: * **IP class and history.** DataDome maintains its own reputation database. A datacenter ASN with prior abuse gets a much lower starting score than a residential IP that has browsed normally. * **TLS + header coherence.** If your JA3 says "Chrome 120" but your `User-Agent` says Firefox, or your `Accept-Language` header is missing when a real Chrome would send one, that mismatch is cheap to detect and expensive for you. * **Client JS signals.** Canvas hash, WebGL vendor/renderer strings, and automation flags like `navigator.webdriver`. Because IP reputation is weighted so heavily, DataDome is the classic case where the exact same browser fingerprint passes on a clean residential IP and fails on a burned one. Our writeup on the [DataDome solver](https://capzy.ai/solvers/datadome) goes into the cookie and slider flow in more detail. # PerimeterX (HUMAN) PerimeterX identifies itself with `_px`, `_px2`, `_px3`, and `_pxvid` cookies. The core token is `_px3`, which encodes a risk verdict. When you fail, you land on a "Press & Hold" challenge, an interstitial with a button you have to hold down while the sensor collects motion data. PerimeterX is the most behavior-heavy of the three. The press-and-hold is not there to be annoying, it is a controlled window to sample your mouse motion. It watches: * **Motion naturalness.** Velocity profiles, micro-tremor, the curve of the path. A straight linear drag from A to B scores badly because human motion follows a lognormal velocity curve with overshoot and correction. * **Sensor payload coherence.** The JS collects a large telemetry object and posts it. If the collected screen size, UA, and timezone do not agree with each other, the verdict flips. * **Replay detection.** Reusing one recorded, frozen mouse trace across many solves is a known failure mode. Modern PerimeterX flags a trajectory that is statistically identical each time. The press-and-hold button often lives inside a **closed shadow DOM**, so you cannot read its state from the page. Solving it reliably means generating fresh, natural motion per attempt on a clean IP. See the [PerimeterX solver](https://capzy.ai/solvers/perimeterx) notes. # Kasada Kasada is the quietest and, in some ways, the strictest. Its signatures are the `x-kpsdk-ct` and `x-kpsdk-cd` headers plus a `KP_UIDz` cookie. The flow is a two-part proof: the client runs an obfuscated VM that produces a **CT** token (a signed client-trust value) and a per-request **CD** proof of work. The trap for scrapers is the CD field. You can sometimes mint a valid `x-kpsdk-ct`, but every subsequent request needs a fresh `x-kpsdk-cd` computed by running Kasada's own bytecode. Miss it and the token replays as invalid. Kasada also fingerprints: * **Execution environment.** Its VM probes for headless artifacts, timing anomalies, and function-toString tampering that betrays a patched browser. * **TLS + IP.** Same story as the others: a coherent handshake from a reputable IP is the floor. The [Kasada solver](https://capzy.ai/solvers/kasada) page covers the CT/CD split. # Why one script passes and another fails Here is the pattern that trips people up. You write a scraper, it works on your laptop, then you deploy it to a cloud box and everything 403s. The code did not change. Two things did: your IP moved from a residential ISP to a flagged datacenter ASN, and your TLS fingerprint may have shifted because the request library on the server negotiates a different cipher order than your desktop Chrome. A minimal coherence check before you even worry about a solver: import httpx # Coherent: UA, Accept-Language, and Sec-CH-UA all agree on "Chrome 120 on Windows". headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0.0.0 Safari/537.36", "Accept-Language": "en-US,en;q=0.9", "Sec-CH-UA": '"Chromium";v="120", "Not_A Brand";v="24"', "Sec-CH-UA-Platform": '"Windows"', } # Still not enough on its own: the TLS/JA3 layer below this # must also say "Chrome", which most default clients get wrong. resp = httpx.get("https://example.com", headers=headers) All three managers score IP reputation and fingerprint coherence before they even look at a challenge token. That is why buying a captcha token in isolation does not save you. The token has to be minted in a session whose network and browser fingerprint already look clean. Clean egress from the [Proxies API](https://capzy.ai/proxies) and a coherent build from the [Fingerprint API](https://capzy.ai/fingerprints) are the two levers that move your baseline score before any challenge appears. # Where Capzy fits We treat these as scoring engines, not walls. Our solvers mint tokens inside real browser sessions on reputation-clean IPs, generate fresh behavioral traces, and keep the client fingerprint coherent end to end. Browse the full [solver catalog](https://capzy.ai/solvers) to see coverage across DataDome, PerimeterX, Kasada, and more. Ready to stop guessing why you got blocked? [Create an account](https://capzy.ai/auth/register) and route your first request through a clean session today.",
"selftext_html": "<!-- SC_OFF --><div class="md"><h1>DataDome, PerimeterX, and Kasada: How Modern Bot Managers Fingerprint You</h1> <p>If your requests are getting a 403 with a <code>datadome</code> cookie stapled to the response, or a challenge page from PerimeterX, or a hard block that keeps demanding an <code>x-kpsdk-ct</code> header, you are dealing with the three bot managers that gate a large slice of the modern web. DataDome, PerimeterX (now part of HUMAN), and Kasada fingerprinting all work on the same principle: collect hundreds of signals from your browser and network, score them, and decide whether you look like a person or a program. They just disagree on which signals they trust most.</p> <p>This post walks through what each one actually measures, the field names you will see in traffic, and why the same script passes on one setup and dies on another.</p> <h1>What "fingerprinting" means here</h1> <p>Fingerprinting is not one number. It is a bundle of observations collected across three layers:</p> <ul> <li><strong>Network layer</strong>: your IP address and its reputation, the TLS handshake (JA3/JA4), the HTTP/2 frame ordering.</li> <li><strong>Client layer</strong>: JavaScript that reads your <code>navigator</code> object, canvas and WebGL rendering, screen metrics, timezone, installed fonts, audio stack quirks.</li> <li><strong>Behavioral layer</strong>: mouse paths, scroll cadence, key timing, and how the page was reached.</li> </ul> <p>A bot manager takes all of that, hashes and weights it, and compares it against what a real Chrome on real hardware behind a residential IP produces. Deviation costs you points. Enough points and you get a challenge or a block.</p> <blockquote> <p>The mistake most scrapers make is treating detection as a single wall. It is a scoring engine. You do not need a perfect fingerprint, you need to stay under the threshold on every layer at once.</p> </blockquote> <h1>DataDome</h1> <p>DataDome leans hard on network reputation and a fast client-side JS payload. The tell is the <code>datadome</code> cookie. On a clean pass it gets set and reused. When you trip the scorer, you get a 403 and a challenge (often a slider) served from a <a href="http://geo.captcha-delivery.com"><code>geo.captcha-delivery.com</code></a> style endpoint, and the response carries a fresh <code>datadome</code> value plus a <code>dd</code> JSON blob describing the challenge.</p> <p>What it scores heavily:</p> <ul> <li><strong>IP class and history.</strong> DataDome maintains its own reputation database. A datacenter ASN with prior abuse gets a much lower starting score than a residential IP that has browsed normally.</li> <li><strong>TLS + header coherence.</strong> If your JA3 says "Chrome 120" but your <code>User-Agent</code> says Firefox, or your <code>Accept-Language</code> header is missing when a real Chrome would send one, that mismatch is cheap to detect and expensive for you.</li> <li><strong>Client JS signals.</strong> Canvas hash, WebGL vendor/renderer strings, and automation flags like <code>navigator.webdriver</code>.</li> </ul> <p>Because IP reputation is weighted so heavily, DataDome is the classic case where the exact same browser fingerprint passes on a clean residential IP and fails on a burned one. Our writeup on the <a href="https://capzy.ai/solvers/datadome">DataDome solver</a> goes into the cookie and slider flow in more detail.</p> <h1>PerimeterX (HUMAN)</h1> <p>PerimeterX identifies itself with <code>_px</code>, <code>_px2</code>, <code>_px3</code>, and <code>_pxvid</code> cookies. The core token is <code>_px3</code>, which encodes a risk verdict. When you fail, you land on a "Press & Hold" challenge, an interstitial with a button you have to hold down while the sensor collects motion data.</p> <p>PerimeterX is the most behavior-heavy of the three. The press-and-hold is not there to be annoying, it is a controlled window to sample your mouse motion. It watches:</p> <ul> <li><strong>Motion naturalness.</strong> Velocity profiles, micro-tremor, the curve of the path. A straight linear drag from A to B scores badly because human motion follows a lognormal velocity curve with overshoot and correction.</li> <li><strong>Sensor payload coherence.</strong> The JS collects a large telemetry object and posts it. If the collected screen size, UA, and timezone do not agree with each other, the verdict flips.</li> <li><strong>Replay detection.</strong> Reusing one recorded, frozen mouse trace across many solves is a known failure mode. Modern PerimeterX flags a trajectory that is statistically identical each time.</li> </ul> <p>The press-and-hold button often lives inside a <strong>closed shadow DOM</strong>, so you cannot read its state from the page. Solving it reliably means generating fresh, natural motion per attempt on a clean IP. See the <a href="https://capzy.ai/solvers/perimeterx">PerimeterX solver</a> notes.</p> <h1>Kasada</h1> <p>Kasada is the quietest and, in some ways, the strictest. Its signatures are the <code>x-kpsdk-ct</code> and <code>x-kpsdk-cd</code> headers plus a <code>KP_UIDz</code> cookie. The flow is a two-part proof: the client runs an obfuscated VM that produces a <strong>CT</strong> token (a signed client-trust value) and a per-request <strong>CD</strong> proof of work.</p> <p>The trap for scrapers is the CD field. You can sometimes mint a valid <code>x-kpsdk-ct</code>, but every subsequent request needs a fresh <code>x-kpsdk-cd</code> computed by running Kasada's own bytecode. Miss it and the token replays as invalid. Kasada also fingerprints:</p> <ul> <li><strong>Execution environment.</strong> Its VM probes for headless artifacts, timing anomalies, and function-toString tampering that betrays a patched browser.</li> <li><strong>TLS + IP.</strong> Same story as the others: a coherent handshake from a reputable IP is the floor.</li> </ul> <p>The <a href="https://capzy.ai/solvers/kasada">Kasada solver</a> page covers the CT/CD split.</p> <h1>Why one script passes and another fails</h1> <p>Here is the pattern that trips people up. You write a scraper, it works on your laptop, then you deploy it to a cloud box and everything 403s. The code did not change. Two things did: your IP moved from a residential ISP to a flagged datacenter ASN, and your TLS fingerprint may have shifted because the request library on the server negotiates a different cipher order than your desktop Chrome.</p> <p>A minimal coherence check before you even worry about a solver:</p> <pre><code>import httpx # Coherent: UA, Accept-Language, and Sec-CH-UA all agree on "Chrome 120 on Windows". headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0.0.0 Safari/537.36", "Accept-Language": "en-US,en;q=0.9", "Sec-CH-UA": '"Chromium";v="120", "Not_A Brand";v="24"', "Sec-CH-UA-Platform": '"Windows"', } # Still not enough on its own: the TLS/JA3 layer below this # must also say "Chrome", which most default clients get wrong. resp = httpx.get("https://example.com", headers=headers) </code></pre> <p>All three managers score IP reputation and fingerprint coherence before they even look at a challenge token. That is why buying a captcha token in isolation does not save you. The token has to be minted in a session whose network and browser fingerprint already look clean. Clean egress from the <a href="https://capzy.ai/proxies">Proxies API</a> and a coherent build from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> are the two levers that move your baseline score before any challenge appears.</p> <h1>Where Capzy fits</h1> <p>We treat these as scoring engines, not walls. Our solvers mint tokens inside real browser sessions on reputation-clean IPs, generate fresh behavioral traces, and keep the client fingerprint coherent end to end. Browse the full <a href="https://capzy.ai/solvers">solver catalog</a> to see coverage across DataDome, PerimeterX, Kasada, and more.</p> <p>Ready to stop guessing why you got blocked? <a href="https://capzy.ai/auth/register">Create an account</a> and route your first request through a clean session today.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/datadome-perimeterx-kasada-fingerprinting",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh8ts0/datadome_perimeterx_and_kasada_how_modern_bot/",
"url_overridden_by_dest": "https://capzy.ai/blog/datadome-perimeterx-kasada-fingerprinting",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T16:17:01+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/Wh134TlqyO5RvOL5V-u5Gg6arhhfMLxZPEmaJpFjNh8.jpeg?width=140&height=73&auto=webp&s=56ea65ab4117de20a249bb9929f4caa7014e51fb",
"preview_image": "https://external-preview.redd.it/Wh134TlqyO5RvOL5V-u5Gg6arhhfMLxZPEmaJpFjNh8.jpeg?auto=webp&s=5fa7f64899be97da3f1d1670709e414cce1be8f0",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3kdw",
"fullname": "t3_1vh3kdw",
"scraped_at": "2026-08-18T09:36:55.069441+00:00",
"title": "Akamai Bot Manager Explained: _abck, sensor_data, and How Detection Works",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "# Akamai Bot Manager Explained: _abck, sensor_data, and How Detection Works If you have ever watched an `_abck` cookie sit at `~-1~` no matter what you send, you already know the frustration Akamai Bot Manager is designed to create. Akamai Bot Manager explained in one line: a JavaScript sensor collects telemetry about your browser and behavior, posts it as `sensor_data`, and the server responds by flipping the `_abck` cookie into a validated state. Get the telemetry right and `_abck` reaches its valid form. Get it wrong and it stays stuck, and every protected request bounces. This post breaks down the cookie states, what goes into `sensor_data`, and why a browser-minted token beats a replayed one. # The _abck cookie and its states `_abck` is the cookie that carries Akamai's verdict. It is a long, structured value, and the segment people watch is the numeric marker inside it. Two states matter: * `~-1~`: the sensor has not proven itself yet. This is the default "unverified" state you get on a fresh load before any valid `sensor_data` POST lands. Requests to protected paths from this state get challenged or blocked. * `~0~`: the validated state. Once Akamai accepts your `sensor_data`, the server sets `_abck` with a `~0~` marker, and that cookie now clears protected endpoints. The goal of any Akamai flow is to move `_abck` from `~-1~` to `~0~`. That transition is entirely driven by the quality of the telemetry you post back. >`~0~` is server-set. You do not compute it. You earn it by posting a `sensor_data` payload that Akamai's backend scores as human. That distinction is the whole game. # What sensor_data contains Akamai ships an obfuscated script (often served from a path with a randomized name, referenced by a `bmak` global in page JS). That script instruments the page and builds a `sensor_data` string, then POSTs it. The payload is a delimited, lightly encoded blob that packs together: * **Device and environment fields.** Screen dimensions, timezone offset, `navigator` properties, plugin and codec quirks, a device fingerprint hash. * **A** `ver` **field.** A session-keyed value computed by the sensor's own crypto. This one is a common wall for pure-replay approaches because it is bound to the session and cannot be lifted from another run. * **Behavioral timeline.** Timestamped mouse movements, key events, touch events, focus and blur, and the timing between them. Akamai wants to see a coherent human interaction history, not a single instantaneous event. * **Integrity checks.** Signals about whether the script was tampered with, whether `toString` was patched, whether the runtime looks automated. The sensor typically posts several times as you interact, and the first one or two POSTs are what move `_abck` to `~0~`. # Why detection is hard to fake The reason Akamai is one of the tougher systems is that the pieces are interlocked. You cannot forge the behavioral timeline without also matching the device fields it was collected on, and you cannot forge the `ver` field without running the site's own bmak crypto in the right session. A payload where the screen size, User-Agent, and timezone disagree gets scored as incoherent immediately. There is also a mobile path. Native apps send an `X-acf-sensor-data` header built by Akamai's Bot Manager Premier SDK (BMP) instead of the web sensor. That is a different pipeline with its own encoding, and covering the web path does not cover the mobile one. # Browser-minted vs replayed tokens Two schools of thought exist for beating Akamai: **Replay / transport-only.** You capture a valid `sensor_data` once and try to resend it. This almost always fails on live sites because the `ver` field is session-bound and `_abck` cycles. Transport-only Go implementations that just move bytes tend to stay at `~-1~` because they never mint a fresh, session-valid sensor. **Browser-minted.** You run a real (or realistically patched) Chrome, let the actual bmak script build `sensor_data` in the correct session, add natural mouse motion, and let the server set `~0~`. This works because everything stays coherent by construction. In our testing, headed Chrome on a reputation-clean residential IP with non-linear, minimum-jerk mouse motion reliably drove `_abck` to `~0~` across repeated runs, where a transport-only path stayed stuck. A rough sketch of the state you are chasing: 1. GET protected page -> Set-Cookie: _abck=...~-1~... (unverified) 2. GET bmak sensor script -> browser instruments the page 3. user-like interaction -> sensor builds sensor_data (+ ver) 4. POST sensor_data -> server validates telemetry 5. Set-Cookie: _abck=...~0~... (validated) 6. subsequent requests carry the ~0~ cookie -> pass The mouse motion in step 3 matters more than people expect. A straight A-to-B drag scores badly. Human paths follow a min-jerk trajectory with slight overshoot and correction, and Akamai's behavioral scoring notices the difference. # Why the ~-1~ cookie gets stuck The most common failure people hit is an `_abck` that will not budge off `~-1~` no matter how many times they retry. Retrying does nothing here because the problem is not transient, it is structural. Each of these keeps you stuck: * **You never ran the sensor.** If your client just fetches the protected page and replays a cookie, `sensor_data` is never built for this session, so the server has nothing to validate. `~-1~` is the correct verdict for a session that never proved itself. * **The** `ver` **field is wrong or borrowed.** Lifting `sensor_data` from another run brings a `ver` computed for a different session. The backend recomputes and rejects it, and `_abck` stays unverified. * **The payload is incoherent.** Screen size that disagrees with the User-Agent, a timezone that does not match the IP, or a behavioral timeline with zero real events all get scored as non-human. * **The IP is flagged.** Even a valid payload can be held at `~-1~` if the source IP has poor reputation, because Akamai declines to hand out `~0~` to a suspect network. The fix is never "retry more." It is to mint a fresh, coherent sensor in the actual session on a clean IP. # The IP and fingerprint dependency Even a perfect `sensor_data` can fail from a burned IP. Akamai folds network reputation into the score, so the exact same telemetry that passes from a clean residential ASN can get challenged from a flagged datacenter range. The same goes for fingerprint coherence: if your TLS handshake advertises one browser and your sensor claims another, the mismatch costs you. This is why a token in isolation is not a product. It has to be minted from a session whose IP and fingerprint already look right. Clean egress from the [Proxies API](https://capzy.ai/proxies) plus a coherent device profile from the [Fingerprint API](https://capzy.ai/fingerprints) set the baseline. The [Akamai solver](https://capzy.ai/solvers/akamai) handles the sensor minting and the `~0~` transition on top of that baseline. # Where Capzy fits We mint `_abck` the honest way: real browser sessions running the site's own bmak sensor, fresh natural motion per solve, on reputation-clean IPs, with fingerprints that stay coherent from TLS up to `navigator`. No brittle replay, no stale `ver`. See the [full solver catalog](https://capzy.ai/solvers) for coverage across Akamai and the other major bot managers. Tired of `~-1~`? [Sign up](https://capzy.ai/auth/register) and get a validated session on your first request.",
"selftext_html": "<!-- SC_OFF --><div class="md"><h1>Akamai Bot Manager Explained: _abck, sensor_data, and How Detection Works</h1> <p>If you have ever watched an <code>_abck</code> cookie sit at <code>~-1~</code> no matter what you send, you already know the frustration Akamai Bot Manager is designed to create. Akamai Bot Manager explained in one line: a JavaScript sensor collects telemetry about your browser and behavior, posts it as <code>sensor_data</code>, and the server responds by flipping the <code>_abck</code> cookie into a validated state. Get the telemetry right and <code>_abck</code> reaches its valid form. Get it wrong and it stays stuck, and every protected request bounces.</p> <p>This post breaks down the cookie states, what goes into <code>sensor_data</code>, and why a browser-minted token beats a replayed one.</p> <h1>The _abck cookie and its states</h1> <p><code>_abck</code> is the cookie that carries Akamai's verdict. It is a long, structured value, and the segment people watch is the numeric marker inside it. Two states matter:</p> <ul> <li><code>~-1~</code>: the sensor has not proven itself yet. This is the default "unverified" state you get on a fresh load before any valid <code>sensor_data</code> POST lands. Requests to protected paths from this state get challenged or blocked.</li> <li><code>~0~</code>: the validated state. Once Akamai accepts your <code>sensor_data</code>, the server sets <code>_abck</code> with a <code>~0~</code> marker, and that cookie now clears protected endpoints.</li> </ul> <p>The goal of any Akamai flow is to move <code>_abck</code> from <code>~-1~</code> to <code>~0~</code>. That transition is entirely driven by the quality of the telemetry you post back.</p> <blockquote> <p><code>~0~</code> is server-set. You do not compute it. You earn it by posting a <code>sensor_data</code> payload that Akamai's backend scores as human. That distinction is the whole game.</p> </blockquote> <h1>What sensor_data contains</h1> <p>Akamai ships an obfuscated script (often served from a path with a randomized name, referenced by a <code>bmak</code> global in page JS). That script instruments the page and builds a <code>sensor_data</code> string, then POSTs it. The payload is a delimited, lightly encoded blob that packs together:</p> <ul> <li><strong>Device and environment fields.</strong> Screen dimensions, timezone offset, <code>navigator</code> properties, plugin and codec quirks, a device fingerprint hash.</li> <li><strong>A</strong> <code>ver</code> <strong>field.</strong> A session-keyed value computed by the sensor's own crypto. This one is a common wall for pure-replay approaches because it is bound to the session and cannot be lifted from another run.</li> <li><strong>Behavioral timeline.</strong> Timestamped mouse movements, key events, touch events, focus and blur, and the timing between them. Akamai wants to see a coherent human interaction history, not a single instantaneous event.</li> <li><strong>Integrity checks.</strong> Signals about whether the script was tampered with, whether <code>toString</code> was patched, whether the runtime looks automated.</li> </ul> <p>The sensor typically posts several times as you interact, and the first one or two POSTs are what move <code>_abck</code> to <code>~0~</code>.</p> <h1>Why detection is hard to fake</h1> <p>The reason Akamai is one of the tougher systems is that the pieces are interlocked. You cannot forge the behavioral timeline without also matching the device fields it was collected on, and you cannot forge the <code>ver</code> field without running the site's own bmak crypto in the right session. A payload where the screen size, User-Agent, and timezone disagree gets scored as incoherent immediately.</p> <p>There is also a mobile path. Native apps send an <code>X-acf-sensor-data</code> header built by Akamai's Bot Manager Premier SDK (BMP) instead of the web sensor. That is a different pipeline with its own encoding, and covering the web path does not cover the mobile one.</p> <h1>Browser-minted vs replayed tokens</h1> <p>Two schools of thought exist for beating Akamai:</p> <p><strong>Replay / transport-only.</strong> You capture a valid <code>sensor_data</code> once and try to resend it. This almost always fails on live sites because the <code>ver</code> field is session-bound and <code>_abck</code> cycles. Transport-only Go implementations that just move bytes tend to stay at <code>~-1~</code> because they never mint a fresh, session-valid sensor.</p> <p><strong>Browser-minted.</strong> You run a real (or realistically patched) Chrome, let the actual bmak script build <code>sensor_data</code> in the correct session, add natural mouse motion, and let the server set <code>~0~</code>. This works because everything stays coherent by construction. In our testing, headed Chrome on a reputation-clean residential IP with non-linear, minimum-jerk mouse motion reliably drove <code>_abck</code> to <code>~0~</code> across repeated runs, where a transport-only path stayed stuck.</p> <p>A rough sketch of the state you are chasing:</p> <pre><code>1. GET protected page -> Set-Cookie: _abck=...~-1~... (unverified) 2. GET bmak sensor script -> browser instruments the page 3. user-like interaction -> sensor builds sensor_data (+ ver) 4. POST sensor_data -> server validates telemetry 5. Set-Cookie: _abck=...~0~... (validated) 6. subsequent requests carry the ~0~ cookie -> pass </code></pre> <p>The mouse motion in step 3 matters more than people expect. A straight A-to-B drag scores badly. Human paths follow a min-jerk trajectory with slight overshoot and correction, and Akamai's behavioral scoring notices the difference.</p> <h1>Why the ~-1~ cookie gets stuck</h1> <p>The most common failure people hit is an <code>_abck</code> that will not budge off <code>~-1~</code> no matter how many times they retry. Retrying does nothing here because the problem is not transient, it is structural. Each of these keeps you stuck:</p> <ul> <li><strong>You never ran the sensor.</strong> If your client just fetches the protected page and replays a cookie, <code>sensor_data</code> is never built for this session, so the server has nothing to validate. <code>~-1~</code> is the correct verdict for a session that never proved itself.</li> <li><strong>The</strong> <code>ver</code> <strong>field is wrong or borrowed.</strong> Lifting <code>sensor_data</code> from another run brings a <code>ver</code> computed for a different session. The backend recomputes and rejects it, and <code>_abck</code> stays unverified.</li> <li><strong>The payload is incoherent.</strong> Screen size that disagrees with the User-Agent, a timezone that does not match the IP, or a behavioral timeline with zero real events all get scored as non-human.</li> <li><strong>The IP is flagged.</strong> Even a valid payload can be held at <code>~-1~</code> if the source IP has poor reputation, because Akamai declines to hand out <code>~0~</code> to a suspect network.</li> </ul> <p>The fix is never "retry more." It is to mint a fresh, coherent sensor in the actual session on a clean IP.</p> <h1>The IP and fingerprint dependency</h1> <p>Even a perfect <code>sensor_data</code> can fail from a burned IP. Akamai folds network reputation into the score, so the exact same telemetry that passes from a clean residential ASN can get challenged from a flagged datacenter range. The same goes for fingerprint coherence: if your TLS handshake advertises one browser and your sensor claims another, the mismatch costs you. This is why a token in isolation is not a product. It has to be minted from a session whose IP and fingerprint already look right.</p> <p>Clean egress from the <a href="https://capzy.ai/proxies">Proxies API</a> plus a coherent device profile from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> set the baseline. The <a href="https://capzy.ai/solvers/akamai">Akamai solver</a> handles the sensor minting and the <code>~0~</code> transition on top of that baseline.</p> <h1>Where Capzy fits</h1> <p>We mint <code>_abck</code> the honest way: real browser sessions running the site's own bmak sensor, fresh natural motion per solve, on reputation-clean IPs, with fingerprints that stay coherent from TLS up to <code>navigator</code>. No brittle replay, no stale <code>ver</code>. See the <a href="https://capzy.ai/solvers">full solver catalog</a> for coverage across Akamai and the other major bot managers.</p> <p>Tired of <code>~-1~</code>? <a href="https://capzy.ai/auth/register">Sign up</a> and get a validated session on your first request.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/akamai-bot-manager-explained",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3kdw/akamai_bot_manager_explained_abck_sensor_data_and/",
"url_overridden_by_dest": "https://capzy.ai/blog/akamai-bot-manager-explained",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 1,
"created_at": "2026-08-06T12:59:27+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/JbE-oQAZC1l6KP2DruAS3_6XTvLK0g27f0pcF7ng6ho.jpeg?width=140&height=73&auto=webp&s=dd35c8faae140ce24dca924b4e2ec580832bf6aa",
"preview_image": "https://external-preview.redd.it/JbE-oQAZC1l6KP2DruAS3_6XTvLK0g27f0pcF7ng6ho.jpeg?auto=webp&s=ad816ce00a4641e2383147806976371b9a2014eb",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3jyb",
"fullname": "t3_1vh3jyb",
"scraped_at": "2026-08-18T09:36:55.069447+00:00",
"title": "Proxyless vs Proxy Solver Tasks: When Each Wins",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "Many Solver task types come in two flavors: a proxy variant where you supply an egress, and a `...TaskProxyless` variant where the solver mints the token on its own infrastructure. Choosing between them is not a preference, it is dictated by the captcha. Some challenges are indifferent to which IP produced the token; others bind the token to the exact IP and cookies it was created with, and using the wrong variant on the second kind fails every time. The rule is short. If the captcha is not IP-bound, use the Proxyless variant and let the solver produce the token cheaply on its own infra. If the challenge is IP- or cookie-bound, use the proxy variant so the token is minted from, and used on, the same egress. This post explains which challenges fall where, why the mismatch fails, and how the [Proxies API](https://capzy.ai/proxies) covers the bound cases. # What "IP-bound" actually means A captcha token is IP-bound when the site validates it against the address that requested the challenge. The site remembers "I issued this challenge to IP X" and, when the token comes back, checks that it is being redeemed from IP X, sometimes alongside a session cookie set during the same exchange. If those do not match, the token is rejected no matter how correct the solve was. A non-bound captcha does not do this. The token stands on its own; the site accepts it from any address because validation does not tie the token to the requesting IP. That difference is the entire basis for choosing a variant. # Proxyless: when the captcha does not care about IP For non-IP-bound challenges, the Proxyless variant is the right default. The solver mints the token on its own infrastructure, you do not supply an egress, and you do not spend proxy bandwidth. Turnstile and reCAPTCHA are common examples where a proxyless mint often works, because in many configurations the token is not pinned to the requesting IP. curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "TurnstileTaskProxyless", "websiteURL": "https://example.com/login", "websiteKey": "0x4AAAAAAA..." } }' Notice there is no proxy block. That is the whole point: less to configure, nothing to pay for on the network side, and no egress to keep healthy. >Proxyless is not "worse" or "cheaper-but-weaker." For a non-IP-bound captcha it is simply correct. Adding a proxy there buys you nothing. # Proxy tasks: when the token is bound to an egress Now the other side. Challenges from systems like DataDome, PerimeterX, and Yidun, along with some WAF clearances, bind the token or cookie to the IP that produced it. For these you must supply a proxy, and critically, you must then use the resulting token from that same egress. The token and the IP travel together. curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "DataDomeTask", "websiteURL": "https://example.com/", "proxyType": "http", "proxyAddress": "203.0.113.10", "proxyPort": 8080, "proxyLogin": "user", "proxyPassword": "pass" } }' The solver mints the token from that egress, and you continue your session from the same egress. Break that link, solve on one IP and submit from another, and the site rejects the token because the binding no longer holds. # Why proxyless fails on an IP-bound captcha This is the mistake worth burning into memory. If you run a Proxyless task against an IP-bound challenge, the solver mints a perfectly valid token from its own address. You then try to use that token from your address. The site issued the challenge expecting redemption from the solver's IP, sees it redeemed from yours, and rejects it. The token was not wrong. The binding was violated. No amount of retrying fixes a category error; you need the proxy variant so mint and use share one egress. The symptom is distinctive: solves "succeed" (you get a token) but the site rejects every one of them. When that happens on a known IP-bound system, the first thing to check is whether you accidentally used a Proxyless variant. # How to choose, concretely A short decision procedure: * Identify the challenge. Is it Turnstile or reCAPTCHA, or is it a DataDome / PerimeterX / Yidun / WAF-style system? * If it is a non-IP-bound type and your configuration does not pin the token, prefer the `...TaskProxyless` variant. Less config, no bandwidth. * If it is IP- or cookie-bound, use the proxy variant and carry the same egress through to the request that uses the token. * If you are unsure whether a given site binds the token, test proxyless first; if valid-looking tokens are uniformly rejected, switch to the proxy variant. For the bound cases, the [Proxies API](https://capzy.ai/proxies) is how you supply a clean, appropriate egress, and keeping the mint and the use on the same address is the part you must not skip. For challenges where the site also renders and mints client-side, pair this with a real engine like the [Cloud Browser](https://capzy.ai/browser) or [Capium](https://capzy.ai/capium), and a coherent identity from the [Fingerprint API](https://capzy.ai/fingerprints). # Closing Proxyless versus proxy is not a knob you tune for cost; it is a property of the captcha you are facing. Non-bound challenges win with the Proxyless variant because it is simpler and free of egress overhead. Bound challenges require the proxy variant because the token and the IP are one unit. Match the variant to the binding and rejections stop; mismatch it and no retry will save you. The full set of variants is listed at [capzy.ai/solvers](https://capzy.ai/solvers). Want to test both paths on your own targets? [Create a free account](https://capzy.ai/auth/register) and run a Proxyless task and a proxy task side by side.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>Many Solver task types come in two flavors: a proxy variant where you supply an egress, and a <code>...TaskProxyless</code> variant where the solver mints the token on its own infrastructure. Choosing between them is not a preference, it is dictated by the captcha. Some challenges are indifferent to which IP produced the token; others bind the token to the exact IP and cookies it was created with, and using the wrong variant on the second kind fails every time.</p> <p>The rule is short. If the captcha is not IP-bound, use the Proxyless variant and let the solver produce the token cheaply on its own infra. If the challenge is IP- or cookie-bound, use the proxy variant so the token is minted from, and used on, the same egress. This post explains which challenges fall where, why the mismatch fails, and how the <a href="https://capzy.ai/proxies">Proxies API</a> covers the bound cases.</p> <h1>What "IP-bound" actually means</h1> <p>A captcha token is IP-bound when the site validates it against the address that requested the challenge. The site remembers "I issued this challenge to IP X" and, when the token comes back, checks that it is being redeemed from IP X, sometimes alongside a session cookie set during the same exchange. If those do not match, the token is rejected no matter how correct the solve was.</p> <p>A non-bound captcha does not do this. The token stands on its own; the site accepts it from any address because validation does not tie the token to the requesting IP. That difference is the entire basis for choosing a variant.</p> <h1>Proxyless: when the captcha does not care about IP</h1> <p>For non-IP-bound challenges, the Proxyless variant is the right default. The solver mints the token on its own infrastructure, you do not supply an egress, and you do not spend proxy bandwidth. Turnstile and reCAPTCHA are common examples where a proxyless mint often works, because in many configurations the token is not pinned to the requesting IP.</p> <pre><code>curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "TurnstileTaskProxyless", "websiteURL": "https://example.com/login", "websiteKey": "0x4AAAAAAA..." } }' </code></pre> <p>Notice there is no proxy block. That is the whole point: less to configure, nothing to pay for on the network side, and no egress to keep healthy.</p> <blockquote> <p>Proxyless is not "worse" or "cheaper-but-weaker." For a non-IP-bound captcha it is simply correct. Adding a proxy there buys you nothing.</p> </blockquote> <h1>Proxy tasks: when the token is bound to an egress</h1> <p>Now the other side. Challenges from systems like DataDome, PerimeterX, and Yidun, along with some WAF clearances, bind the token or cookie to the IP that produced it. For these you must supply a proxy, and critically, you must then use the resulting token from that same egress. The token and the IP travel together.</p> <pre><code>curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "DataDomeTask", "websiteURL": "https://example.com/", "proxyType": "http", "proxyAddress": "203.0.113.10", "proxyPort": 8080, "proxyLogin": "user", "proxyPassword": "pass" } }' </code></pre> <p>The solver mints the token from that egress, and you continue your session from the same egress. Break that link, solve on one IP and submit from another, and the site rejects the token because the binding no longer holds.</p> <h1>Why proxyless fails on an IP-bound captcha</h1> <p>This is the mistake worth burning into memory. If you run a Proxyless task against an IP-bound challenge, the solver mints a perfectly valid token from its own address. You then try to use that token from your address. The site issued the challenge expecting redemption from the solver's IP, sees it redeemed from yours, and rejects it. The token was not wrong. The binding was violated. No amount of retrying fixes a category error; you need the proxy variant so mint and use share one egress.</p> <p>The symptom is distinctive: solves "succeed" (you get a token) but the site rejects every one of them. When that happens on a known IP-bound system, the first thing to check is whether you accidentally used a Proxyless variant.</p> <h1>How to choose, concretely</h1> <p>A short decision procedure:</p> <ul> <li>Identify the challenge. Is it Turnstile or reCAPTCHA, or is it a DataDome / PerimeterX / Yidun / WAF-style system?</li> <li>If it is a non-IP-bound type and your configuration does not pin the token, prefer the <code>...TaskProxyless</code> variant. Less config, no bandwidth.</li> <li>If it is IP- or cookie-bound, use the proxy variant and carry the same egress through to the request that uses the token.</li> <li>If you are unsure whether a given site binds the token, test proxyless first; if valid-looking tokens are uniformly rejected, switch to the proxy variant.</li> </ul> <p>For the bound cases, the <a href="https://capzy.ai/proxies">Proxies API</a> is how you supply a clean, appropriate egress, and keeping the mint and the use on the same address is the part you must not skip. For challenges where the site also renders and mints client-side, pair this with a real engine like the <a href="https://capzy.ai/browser">Cloud Browser</a> or <a href="https://capzy.ai/capium">Capium</a>, and a coherent identity from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a>.</p> <h1>Closing</h1> <p>Proxyless versus proxy is not a knob you tune for cost; it is a property of the captcha you are facing. Non-bound challenges win with the Proxyless variant because it is simpler and free of egress overhead. Bound challenges require the proxy variant because the token and the IP are one unit. Match the variant to the binding and rejections stop; mismatch it and no retry will save you. The full set of variants is listed at <a href="https://capzy.ai/solvers">capzy.ai/solvers</a>.</p> <p>Want to test both paths on your own targets? <a href="https://capzy.ai/auth/register">Create a free account</a> and run a Proxyless task and a proxy task side by side.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/proxyless-vs-proxy-solver-tasks",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3jyb/proxyless_vs_proxy_solver_tasks_when_each_wins/",
"url_overridden_by_dest": "https://capzy.ai/blog/proxyless-vs-proxy-solver-tasks",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:58:57+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/CL8_Yb2zcQM35ZDkRT-RPP8Jhv2s-FDGCuSl8FPPEx0.jpeg?width=140&height=73&auto=webp&s=aff6e076064c313d8e7e6ac05249eea1fd0871d6",
"preview_image": "https://external-preview.redd.it/CL8_Yb2zcQM35ZDkRT-RPP8Jhv2s-FDGCuSl8FPPEx0.jpeg?auto=webp&s=7a7c3ba8cd3ce2f468c638be2703e9dbd94a21e7",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3js3",
"fullname": "t3_1vh3js3",
"scraped_at": "2026-08-18T09:36:55.069454+00:00",
"title": "IP Reputation 101: Why the Same Script Passes on One IP and Fails on Another",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "# IP Reputation 101: Why the Same Script Passes on One IP and Fails on Another You ran the exact same scraper from two machines. On your laptop it sailed through. On the cloud box it got a 403 on the first request. The code is byte-for-byte identical, so what changed? The IP. This is IP reputation 101: every address you connect from carries a history and a classification, and anti-bot systems score that reputation before they read a single header. The same script passes on one IP and fails on another because the two IPs start with different scores. Let me explain what "reputation" is made of and how to keep yours clean. # What an IP's reputation is built from An IP address is not anonymous to defenders. It comes with metadata that is cheap to look up and heavily weighted. # ASN and IP class Every IP belongs to an Autonomous System Number, which tells a defender who operates the range. The broad classes: * **Residential.** Addresses assigned by consumer ISPs to homes. These carry the highest trust because that is where real users live. * **Datacenter.** Addresses owned by hosting and cloud providers. A request from one of these is, by default, not a person sitting at a desk, so it starts with a lower score. Well-known cloud ranges are especially scrutinized because so much automated traffic originates there. * **Mobile.** Addresses from cellular carriers, often shared behind carrier-grade NAT. High trust, but shared so heavily that a single IP maps to many users. When you move a script from a home connection to a cloud VM, you move from the highest-trust class to one of the lowest. That single change explains most "it worked yesterday" blocks. # History and abuse signals Beyond class, defenders track behavior over time. Has this IP been seen hammering endpoints? Is it on public blocklists? Has it failed challenges repeatedly? Large bot managers like DataDome and Akamai maintain their own reputation databases fed by traffic across every site they protect, so an IP burned on one property arrives pre-flagged on the next. >Reputation is shared and sticky. An IP that got burned scraping one site last week can start you at the floor on a completely different site today, because the same vendor protects both. # Geo coherence Your IP has a geolocation, and everything else you present should agree with it. A US East Coast IP paired with an `Asia/Tokyo` timezone and an `Accept-Language: de-DE` header is three contradictions in one request. Defenders check that your IP country, your declared timezone, and your language line up. # Contradiction: US IP, Tokyo timezone, German language. # Each mismatch spends score even before any challenge. ip_country = "US" declared_tz = "Asia/Tokyo" # wrong for a US IP accept_language = "de-DE,de;q=0.9" # wrong for a US IP # Coherent: pick egress, timezone, and language together. ip_country = "US" declared_tz = "America/New_York" accept_language = "en-US,en;q=0.9" # Why the score moves before your code runs The important mental model: IP reputation is a **network-layer** signal. It is evaluated during the connection, before your headers are parsed and long before any JavaScript or captcha appears. That is why a perfect browser fingerprint cannot rescue a burned IP. You already lost points at the handshake. This also explains a confusing symptom, where you get blocked with no challenge at all. A challenge is a chance to prove yourself. If your starting score is low enough, the defender skips the chance and blocks outright. No slider, no press-and-hold, just a 403. # Shared IPs and the noisy-neighbor problem Cheap proxy pools recycle a small set of addresses across thousands of customers. When someone else abuses an IP you are also using, you inherit the flag. This is the noisy-neighbor problem, and it is why a "working" proxy suddenly stops working with no change on your end. The address did not change. Its reputation did, because of traffic that was not yours. The fix is egress you can trust: reputation-clean addresses, geo-matched to your target, rotated sensibly rather than hammered. A managed [Proxies API](https://capzy.ai/proxies) exists to solve exactly this, giving you clean addresses without you having to audit ASN history by hand. # Rotation done wrong makes things worse A lot of people reach for aggressive rotation as a fix and end up hurting their score. Rotating your IP on every single request is not what a human does. A real user holds one address for a whole session, clicking through pages, building up cookies and a coherent history. A scraper that swaps IP on every request looks exactly like what it is: a distributed effort to dodge counting. Some defenders specifically flag a session where the cookie set stays constant but the source IP changes on each hit, because that pairing is impossible for a real browser. The healthier pattern is sticky sessions. Hold one clean IP for the duration of a logical session, pace your requests, and only rotate when you see signs the address is getting flagged, a sudden challenge, a 403, a slower response. Rotate on failure, not on a fixed timer. This keeps each session internally coherent while still spreading load across your pool over time. # Reading the symptoms Different block styles tell you where you stand on reputation: * **Instant 403 with no challenge.** Your starting score was too low to bother challenging. Almost always an IP-class or reputation problem. * **A challenge on the first request.** Your score was borderline. The defender is giving you a chance to prove yourself, which often means the IP is acceptable but something else (fingerprint, headers) is shaky. * **Passing for a while, then blocks.** You burned the IP through volume or pace, or a noisy neighbor did it for you. Time to rotate that address out. Learning to read these saves hours. A no-challenge 403 is never solved by tuning your solver, because the solver never got to run. # How to keep your reputation clean A short checklist that prevents most reputation-driven blocks: * **Match IP class to the target.** Consumer-facing sites expect consumer-class traffic. Do not scrape a retail site from a well-known cloud range. * **Match geo end to end.** Egress country, timezone, and language should all agree. If you rotate country, rotate the timezone and language with it. * **Do not hammer a single IP.** Bursts of identical requests from one address burn it fast. Spread load and pace requests like a human session. * **Rotate on failure, not on every request.** Sticky sessions look more human than an address that changes mid-session. Rotate when an IP shows signs of being flagged, not on a fixed per-request timer. * **Keep the rest coherent.** Reputation gets you in the door, but a clean IP paired with an obvious Python TLS fingerprint still fails. Pair clean egress with a coherent build from the [Fingerprint API](https://capzy.ai/fingerprints). # Why a token alone will not save you If you buy a captcha token but mint it from a burned IP, the token is often rejected on validation because the defender scores the session it was solved in. DataDome, Akamai, PerimeterX, and Kasada all fold IP reputation into the verdict. The token has to come from a session whose network reputation already looks clean. That is why our solvers run on reputation-clean egress by default. Explore the [solver catalog](https://capzy.ai/solvers) to see how the pieces fit. # Where Capzy fits Capzy pairs clean, geo-coherent egress with coherent fingerprints and browser-minted tokens, so your session starts with a good score instead of fighting uphill from the floor. The [Proxies API](https://capzy.ai/proxies) handles the network layer so you stop debugging blocks that were never about your code. Same script, different result? It is almost always the IP. [Sign up](https://capzy.ai/auth/register) and route through clean egress on your next request.",
"selftext_html": "<!-- SC_OFF --><div class="md"><h1>IP Reputation 101: Why the Same Script Passes on One IP and Fails on Another</h1> <p>You ran the exact same scraper from two machines. On your laptop it sailed through. On the cloud box it got a 403 on the first request. The code is byte-for-byte identical, so what changed? The IP. This is IP reputation 101: every address you connect from carries a history and a classification, and anti-bot systems score that reputation before they read a single header. The same script passes on one IP and fails on another because the two IPs start with different scores.</p> <p>Let me explain what "reputation" is made of and how to keep yours clean.</p> <h1>What an IP's reputation is built from</h1> <p>An IP address is not anonymous to defenders. It comes with metadata that is cheap to look up and heavily weighted.</p> <h1>ASN and IP class</h1> <p>Every IP belongs to an Autonomous System Number, which tells a defender who operates the range. The broad classes:</p> <ul> <li><strong>Residential.</strong> Addresses assigned by consumer ISPs to homes. These carry the highest trust because that is where real users live.</li> <li><strong>Datacenter.</strong> Addresses owned by hosting and cloud providers. A request from one of these is, by default, not a person sitting at a desk, so it starts with a lower score. Well-known cloud ranges are especially scrutinized because so much automated traffic originates there.</li> <li><strong>Mobile.</strong> Addresses from cellular carriers, often shared behind carrier-grade NAT. High trust, but shared so heavily that a single IP maps to many users.</li> </ul> <p>When you move a script from a home connection to a cloud VM, you move from the highest-trust class to one of the lowest. That single change explains most "it worked yesterday" blocks.</p> <h1>History and abuse signals</h1> <p>Beyond class, defenders track behavior over time. Has this IP been seen hammering endpoints? Is it on public blocklists? Has it failed challenges repeatedly? Large bot managers like DataDome and Akamai maintain their own reputation databases fed by traffic across every site they protect, so an IP burned on one property arrives pre-flagged on the next.</p> <blockquote> <p>Reputation is shared and sticky. An IP that got burned scraping one site last week can start you at the floor on a completely different site today, because the same vendor protects both.</p> </blockquote> <h1>Geo coherence</h1> <p>Your IP has a geolocation, and everything else you present should agree with it. A US East Coast IP paired with an <code>Asia/Tokyo</code> timezone and an <code>Accept-Language: de-DE</code> header is three contradictions in one request. Defenders check that your IP country, your declared timezone, and your language line up.</p> <pre><code># Contradiction: US IP, Tokyo timezone, German language. # Each mismatch spends score even before any challenge. ip_country = "US" declared_tz = "Asia/Tokyo" # wrong for a US IP accept_language = "de-DE,de;q=0.9" # wrong for a US IP # Coherent: pick egress, timezone, and language together. ip_country = "US" declared_tz = "America/New_York" accept_language = "en-US,en;q=0.9" </code></pre> <h1>Why the score moves before your code runs</h1> <p>The important mental model: IP reputation is a <strong>network-layer</strong> signal. It is evaluated during the connection, before your headers are parsed and long before any JavaScript or captcha appears. That is why a perfect browser fingerprint cannot rescue a burned IP. You already lost points at the handshake.</p> <p>This also explains a confusing symptom, where you get blocked with no challenge at all. A challenge is a chance to prove yourself. If your starting score is low enough, the defender skips the chance and blocks outright. No slider, no press-and-hold, just a 403.</p> <h1>Shared IPs and the noisy-neighbor problem</h1> <p>Cheap proxy pools recycle a small set of addresses across thousands of customers. When someone else abuses an IP you are also using, you inherit the flag. This is the noisy-neighbor problem, and it is why a "working" proxy suddenly stops working with no change on your end. The address did not change. Its reputation did, because of traffic that was not yours.</p> <p>The fix is egress you can trust: reputation-clean addresses, geo-matched to your target, rotated sensibly rather than hammered. A managed <a href="https://capzy.ai/proxies">Proxies API</a> exists to solve exactly this, giving you clean addresses without you having to audit ASN history by hand.</p> <h1>Rotation done wrong makes things worse</h1> <p>A lot of people reach for aggressive rotation as a fix and end up hurting their score. Rotating your IP on every single request is not what a human does. A real user holds one address for a whole session, clicking through pages, building up cookies and a coherent history. A scraper that swaps IP on every request looks exactly like what it is: a distributed effort to dodge counting. Some defenders specifically flag a session where the cookie set stays constant but the source IP changes on each hit, because that pairing is impossible for a real browser.</p> <p>The healthier pattern is sticky sessions. Hold one clean IP for the duration of a logical session, pace your requests, and only rotate when you see signs the address is getting flagged, a sudden challenge, a 403, a slower response. Rotate on failure, not on a fixed timer. This keeps each session internally coherent while still spreading load across your pool over time.</p> <h1>Reading the symptoms</h1> <p>Different block styles tell you where you stand on reputation:</p> <ul> <li><strong>Instant 403 with no challenge.</strong> Your starting score was too low to bother challenging. Almost always an IP-class or reputation problem.</li> <li><strong>A challenge on the first request.</strong> Your score was borderline. The defender is giving you a chance to prove yourself, which often means the IP is acceptable but something else (fingerprint, headers) is shaky.</li> <li><strong>Passing for a while, then blocks.</strong> You burned the IP through volume or pace, or a noisy neighbor did it for you. Time to rotate that address out.</li> </ul> <p>Learning to read these saves hours. A no-challenge 403 is never solved by tuning your solver, because the solver never got to run.</p> <h1>How to keep your reputation clean</h1> <p>A short checklist that prevents most reputation-driven blocks:</p> <ul> <li><strong>Match IP class to the target.</strong> Consumer-facing sites expect consumer-class traffic. Do not scrape a retail site from a well-known cloud range.</li> <li><strong>Match geo end to end.</strong> Egress country, timezone, and language should all agree. If you rotate country, rotate the timezone and language with it.</li> <li><strong>Do not hammer a single IP.</strong> Bursts of identical requests from one address burn it fast. Spread load and pace requests like a human session.</li> <li><strong>Rotate on failure, not on every request.</strong> Sticky sessions look more human than an address that changes mid-session. Rotate when an IP shows signs of being flagged, not on a fixed per-request timer.</li> <li><strong>Keep the rest coherent.</strong> Reputation gets you in the door, but a clean IP paired with an obvious Python TLS fingerprint still fails. Pair clean egress with a coherent build from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a>.</li> </ul> <h1>Why a token alone will not save you</h1> <p>If you buy a captcha token but mint it from a burned IP, the token is often rejected on validation because the defender scores the session it was solved in. DataDome, Akamai, PerimeterX, and Kasada all fold IP reputation into the verdict. The token has to come from a session whose network reputation already looks clean. That is why our solvers run on reputation-clean egress by default. Explore the <a href="https://capzy.ai/solvers">solver catalog</a> to see how the pieces fit.</p> <h1>Where Capzy fits</h1> <p>Capzy pairs clean, geo-coherent egress with coherent fingerprints and browser-minted tokens, so your session starts with a good score instead of fighting uphill from the floor. The <a href="https://capzy.ai/proxies">Proxies API</a> handles the network layer so you stop debugging blocks that were never about your code.</p> <p>Same script, different result? It is almost always the IP. <a href="https://capzy.ai/auth/register">Sign up</a> and route through clean egress on your next request.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/ip-reputation-101",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3js3/ip_reputation_101_why_the_same_script_passes_on/",
"url_overridden_by_dest": "https://capzy.ai/blog/ip-reputation-101",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:58:45+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/TlZnwbbO23GVjUWN4jnBE9mLaHmlhbccsIngmw6LzTg.jpeg?width=140&height=73&auto=webp&s=eb16434d41193b9b6d22197ff9b41abaee229b99",
"preview_image": "https://external-preview.redd.it/TlZnwbbO23GVjUWN4jnBE9mLaHmlhbccsIngmw6LzTg.jpeg?auto=webp&s=49e351faa1b1c76b92ebb2ba87ec7d81959fa4f0",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3ia5",
"fullname": "t3_1vh3ia5",
"scraped_at": "2026-08-18T09:36:55.069460+00:00",
"title": "Solving reCAPTCHA and Turnstile in Mobile Apps and WebViews",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "A lot of mobile apps do not roll their own captcha. They render a web widget inside a WebView, or they call a web endpoint that expects a token, which means the captcha you are looking at is the same reCAPTCHA or Turnstile you would meet in a browser. That is good news: once you locate the public sitekey, you can solve it with the ordinary web task type and feed the token back into the app's flow. The trick is knowing where to look on mobile and how to hand the token back. The workflow is: find the public sitekey in the WebView's page or in the app's API traffic, create a Solver task with `websiteURL` set to the WebView or page URL and `websiteKey` set to that sitekey, then inject the returned token into the WebView form or pass it to the backend call the app was going to make. The two mobile-specific wrinkles are that native SDK captchas differ from web ones, and that your User-Agent and fingerprint have to match the app's WebView. Both are covered below. # Where the sitekey lives on mobile On the web you read the sitekey out of the widget markup. On mobile it is in one of two places. If the captcha renders in a WebView, the sitekey is in that WebView's HTML, exactly as it would be in a browser: a `data-sitekey` on a `.g-recaptcha`/`.cf-turnstile` element, or a `sitekey` parameter in the widget's script URL. Point a debugger at the WebView, or inspect the page it loads, and the key is right there. If the app talks to a web endpoint directly, the sitekey shows up in the app's API traffic. Proxy the app's network calls and watch for the captcha provider's domains; the sitekey travels in the request or response that sets up the challenge. Either way you are hunting for the same public string. >The public sitekey is not a secret and is safe to read; it is meant to be visible to the client. What you should not do is try to defeat an access control you were not authorized to test. Find the key, do not forge the flow. # Solve it with the web task type Once you have the sitekey and the URL, this is a normal web solve. Use the matching web task type: `RecaptchaV2Task`, `RecaptchaV3Task`, or `TurnstileTask`. Set `websiteURL` to the WebView page URL (or the web endpoint URL the app uses) and `websiteKey` to the sitekey you found. # createTask for a Turnstile widget rendered inside a WebView curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "TurnstileTask", "websiteURL": "https://m.example.com/app/verify", "websiteKey": "0x4AAAAAAA..." } }' # -> { "errorId": 0, "taskId": "7f21-..." } # getTaskResult until ready curl -s https://api.capzy.ai/getTaskResult \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "taskId": "7f21-..." }' # -> { "status": "ready", "solution": { "token": "0.AB..." } } There is nothing mobile-specific about the solve itself. The Solver API neither knows nor cares that the widget happens to be inside an app; it works from the URL and the sitekey. # Inject the token back into the flow Now hand the token to whatever was going to consume it. Two shapes come up. If the widget lives in a WebView form, inject the token into that form the same way you would in a browser: set the hidden field (`cf-turnstile-response`, `g-recaptcha-response`, or `h-captcha-response`), fire any `data-callback`, and let the form submit. You can run this JavaScript inside the WebView via your automation harness: // executed inside the WebView document const field = document.querySelector('[name="cf-turnstile-response"]'); if (field) { field.value = TOKEN; // TOKEN from getTaskResult field.dispatchEvent(new Event("input", { bubbles: true })); field.dispatchEvent(new Event("change", { bubbles: true })); } const box = document.querySelector(".cf-turnstile"); const cb = box && box.getAttribute("data-callback"); if (cb && typeof window[cb] === "function") window[cb](TOKEN); If instead the app makes a backend call that expects a token, skip the DOM and pass the token into that call. Replay the request the app would have sent, including the token in the field the endpoint expects (`g-recaptcha-response` and friends), alongside the app's other parameters. This is often the cleaner path for a native app, because it does not depend on driving a WebView at all. # The two mobile gotchas First, native SDK captchas are not web captchas. Some apps embed a vendor's native mobile SDK rather than a WebView widget. Those do not expose a web sitekey and cannot be solved with the web task types; the flow, the parameters, and sometimes the token format differ. Before you assume the web path, confirm you are actually looking at a WebView-rendered or web-endpoint challenge. If it is a native SDK, the web `websiteKey` approach does not apply. When in doubt, check the supported types at [capzy.ai/solvers](https://capzy.ai/solvers). Second, your User-Agent and fingerprint must match the app's WebView. A WebView presents a specific User-Agent, often a customized one the app sets, and the site may key its behavior to it. If you solve or replay with a desktop-browser User-Agent while the app uses a mobile WebView string, you introduce an incoherence the site can flag. Match the WebView's User-Agent, and keep the rest of the client identity coherent with it. This is where the [Fingerprint API](https://capzy.ai/fingerprints) earns its place, producing a mobile identity that hangs together instead of a contradictory mix. # The usual honest caveat As on the web, the token is necessary but not sufficient. A mobile flow can bind the challenge to an IP or a session, in which case you also need a clean, matching egress from the [Proxies API](https://capzy.ai/proxies), carried through from mint to use. And when the app's endpoint expects tokens minted by a real engine, a remote [Cloud Browser](https://capzy.ai/browser) or [Capium](https://capzy.ai/capium) gives you a genuine execution environment instead of a bare HTTP replay. The captcha is answered by the [Solver API](https://capzy.ai/solvers); the surrounding coherence is what gets the answer accepted. Mobile is less exotic than it looks. Find the sitekey in the WebView or the API traffic, solve it as a web task with the right URL and key, inject the token into the form or the backend call, and keep your User-Agent and fingerprint honest to the app's WebView. [Create a free account](https://capzy.ai/auth/register) and solve your first WebView captcha end to end.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>A lot of mobile apps do not roll their own captcha. They render a web widget inside a WebView, or they call a web endpoint that expects a token, which means the captcha you are looking at is the same reCAPTCHA or Turnstile you would meet in a browser. That is good news: once you locate the public sitekey, you can solve it with the ordinary web task type and feed the token back into the app's flow. The trick is knowing where to look on mobile and how to hand the token back.</p> <p>The workflow is: find the public sitekey in the WebView's page or in the app's API traffic, create a Solver task with <code>websiteURL</code> set to the WebView or page URL and <code>websiteKey</code> set to that sitekey, then inject the returned token into the WebView form or pass it to the backend call the app was going to make. The two mobile-specific wrinkles are that native SDK captchas differ from web ones, and that your User-Agent and fingerprint have to match the app's WebView. Both are covered below.</p> <h1>Where the sitekey lives on mobile</h1> <p>On the web you read the sitekey out of the widget markup. On mobile it is in one of two places.</p> <p>If the captcha renders in a WebView, the sitekey is in that WebView's HTML, exactly as it would be in a browser: a <code>data-sitekey</code> on a <code>.g-recaptcha</code>/<code>.cf-turnstile</code> element, or a <code>sitekey</code> parameter in the widget's script URL. Point a debugger at the WebView, or inspect the page it loads, and the key is right there.</p> <p>If the app talks to a web endpoint directly, the sitekey shows up in the app's API traffic. Proxy the app's network calls and watch for the captcha provider's domains; the sitekey travels in the request or response that sets up the challenge. Either way you are hunting for the same public string.</p> <blockquote> <p>The public sitekey is not a secret and is safe to read; it is meant to be visible to the client. What you should not do is try to defeat an access control you were not authorized to test. Find the key, do not forge the flow.</p> </blockquote> <h1>Solve it with the web task type</h1> <p>Once you have the sitekey and the URL, this is a normal web solve. Use the matching web task type: <code>RecaptchaV2Task</code>, <code>RecaptchaV3Task</code>, or <code>TurnstileTask</code>. Set <code>websiteURL</code> to the WebView page URL (or the web endpoint URL the app uses) and <code>websiteKey</code> to the sitekey you found.</p> <pre><code># createTask for a Turnstile widget rendered inside a WebView curl -s https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "task": { "type": "TurnstileTask", "websiteURL": "https://m.example.com/app/verify", "websiteKey": "0x4AAAAAAA..." } }' # -> { "errorId": 0, "taskId": "7f21-..." } # getTaskResult until ready curl -s https://api.capzy.ai/getTaskResult \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_CLIENT_KEY", "taskId": "7f21-..." }' # -> { "status": "ready", "solution": { "token": "0.AB..." } } </code></pre> <p>There is nothing mobile-specific about the solve itself. The Solver API neither knows nor cares that the widget happens to be inside an app; it works from the URL and the sitekey.</p> <h1>Inject the token back into the flow</h1> <p>Now hand the token to whatever was going to consume it. Two shapes come up.</p> <p>If the widget lives in a WebView form, inject the token into that form the same way you would in a browser: set the hidden field (<code>cf-turnstile-response</code>, <code>g-recaptcha-response</code>, or <code>h-captcha-response</code>), fire any <code>data-callback</code>, and let the form submit. You can run this JavaScript inside the WebView via your automation harness:</p> <pre><code>// executed inside the WebView document const field = document.querySelector('[name="cf-turnstile-response"]'); if (field) { field.value = TOKEN; // TOKEN from getTaskResult field.dispatchEvent(new Event("input", { bubbles: true })); field.dispatchEvent(new Event("change", { bubbles: true })); } const box = document.querySelector(".cf-turnstile"); const cb = box && box.getAttribute("data-callback"); if (cb && typeof window[cb] === "function") window[cb](TOKEN); </code></pre> <p>If instead the app makes a backend call that expects a token, skip the DOM and pass the token into that call. Replay the request the app would have sent, including the token in the field the endpoint expects (<code>g-recaptcha-response</code> and friends), alongside the app's other parameters. This is often the cleaner path for a native app, because it does not depend on driving a WebView at all.</p> <h1>The two mobile gotchas</h1> <p>First, native SDK captchas are not web captchas. Some apps embed a vendor's native mobile SDK rather than a WebView widget. Those do not expose a web sitekey and cannot be solved with the web task types; the flow, the parameters, and sometimes the token format differ. Before you assume the web path, confirm you are actually looking at a WebView-rendered or web-endpoint challenge. If it is a native SDK, the web <code>websiteKey</code> approach does not apply. When in doubt, check the supported types at <a href="https://capzy.ai/solvers">capzy.ai/solvers</a>.</p> <p>Second, your User-Agent and fingerprint must match the app's WebView. A WebView presents a specific User-Agent, often a customized one the app sets, and the site may key its behavior to it. If you solve or replay with a desktop-browser User-Agent while the app uses a mobile WebView string, you introduce an incoherence the site can flag. Match the WebView's User-Agent, and keep the rest of the client identity coherent with it. This is where the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> earns its place, producing a mobile identity that hangs together instead of a contradictory mix.</p> <h1>The usual honest caveat</h1> <p>As on the web, the token is necessary but not sufficient. A mobile flow can bind the challenge to an IP or a session, in which case you also need a clean, matching egress from the <a href="https://capzy.ai/proxies">Proxies API</a>, carried through from mint to use. And when the app's endpoint expects tokens minted by a real engine, a remote <a href="https://capzy.ai/browser">Cloud Browser</a> or <a href="https://capzy.ai/capium">Capium</a> gives you a genuine execution environment instead of a bare HTTP replay. The captcha is answered by the <a href="https://capzy.ai/solvers">Solver API</a>; the surrounding coherence is what gets the answer accepted.</p> <p>Mobile is less exotic than it looks. Find the sitekey in the WebView or the API traffic, solve it as a web task with the right URL and key, inject the token into the form or the backend call, and keep your User-Agent and fingerprint honest to the app's WebView. <a href="https://capzy.ai/auth/register">Create a free account</a> and solve your first WebView captcha end to end.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/solve-captcha-mobile-apps-webview",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3ia5/solving_recaptcha_and_turnstile_in_mobile_apps/",
"url_overridden_by_dest": "https://capzy.ai/blog/solve-captcha-mobile-apps-webview",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:56:54+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/ib582dkPX8y2crkkoHHiGicoIAxVtHV9VQUapWDY37M.jpeg?width=140&height=73&auto=webp&s=f92b7bba40447468223c0e17f66928e2aaedbbff",
"preview_image": "https://external-preview.redd.it/ib582dkPX8y2crkkoHHiGicoIAxVtHV9VQUapWDY37M.jpeg?auto=webp&s=499a7507ef6b2d07b6af8f99652bf683c7294787",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3i0j",
"fullname": "t3_1vh3i0j",
"scraped_at": "2026-08-18T09:36:55.069468+00:00",
"title": "TLS and HTTP/2 Fingerprinting (JA3/JA4): The Layer Most Scrapers Miss",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "# TLS and HTTP/2 Fingerprinting (JA3/JA4): The Layer Most Scrapers Miss Your headers are perfect. Your `User-Agent` says Chrome 120, your client hints match, your `Accept-Language` is set. And you still get blocked on the very first request, before any JavaScript runs. The reason is a layer almost every scraper ignores: TLS and HTTP/2 fingerprinting. Before your headers are ever parsed, the way your client negotiates the encrypted connection produces a JA3 or JA4 hash, and if that hash says "Python client" while your header says "Chrome," the contradiction blocks you at the handshake. This is the layer most tutorials skip. Here is how it works and why it matters more than the headers you have been tuning. # What gets fingerprinted in a TLS handshake When any client opens an HTTPS connection, it sends a **ClientHello** that advertises what it supports. That message is far more revealing than people realize. It contains: * **The cipher suite list, in order.** Which encryption algorithms the client offers, and crucially the exact order. Chrome offers a specific ordered list. OpenSSL defaults, which most Python libraries inherit, offer a different one. * **TLS extensions, in order.** ALPN, supported groups, signature algorithms, key share, and more. Both which extensions are present and their ordering vary by client. * **Supported elliptic curves and point formats.** The named groups a client will use for key exchange. * **The TLS version and supported versions.** A JA3 hash is an MD5 of a comma-joined string built from these fields (TLS version, cipher list, extension list, curves, point formats). JA4 is the newer, more structured successor that is harder to spoof and includes ALPN and a sorted-versus-unsorted view of extensions. Either way, the result is a compact fingerprint of your TLS stack. >Your JA3/JA4 is decided by your TLS library, not by the headers you set. You can spoof a User-Agent in one line. You cannot spoof a JA3 hash without changing how the handshake itself is built. # Why default HTTP clients betray you Here is the trap. A default Python `requests` or `httpx` call negotiates TLS through the system OpenSSL. Its cipher order, extension set, and curve list produce a JA3 that no browser emits. So a defender sees a request that claims to be Chrome in the `User-Agent` but has the TLS signature of a scripting language. That mismatch is one of the cheapest, most reliable bot signals in existence, and it fires before your code sends anything else. Your header says: User-Agent: ...Chrome/120... Your JA3 hash says: (a Python/OpenSSL signature no Chrome produces) Defender's conclusion: lying client -> block This is why people report that "adding more headers did nothing." The block was never about headers. It was the handshake. # HTTP/2 fingerprinting Even after TLS, the story continues. Modern browsers speak HTTP/2, and the way a client sets up an HTTP/2 connection is itself a fingerprint. Defenders look at: * **SETTINGS frame values.** The initial header table size, max concurrent streams, initial window size, and the order these settings appear in. Browsers use characteristic values. * **WINDOW\_UPDATE behavior.** How the client manages flow control. * **Pseudo-header order.** The order of `:method`, `:authority`, `:scheme`, `:path` in the HEADERS frame. Browsers use a stable order. Many libraries do not, or fall back to HTTP/1.1 entirely, which is itself a tell against a "modern Chrome" claim. * **Priority and frame ordering.** The overall choreography of frames on the connection. Combined, the TLS ClientHello and the HTTP/2 setup give a defender a network-layer identity that is very hard to fake from a generic client, and that has to agree with the browser identity you claim on top of it. # ALPN and the small details that leak One easy-to-miss field is **ALPN** (Application-Layer Protocol Negotiation), the extension where the client says whether it prefers `h2` (HTTP/2) or `http/1.1`. A client that advertises `http/1.1` only, while claiming to be a current Chrome that always offers `h2`, is another contradiction. GREASE values, the deliberately random extension values Chrome injects to keep the ecosystem flexible, are another. Their presence and pattern are part of the expected Chrome signature, and a client that omits them looks unlike real Chrome. # How to actually fix it You have two honest options: * **Use a client that mimics a real browser's TLS and HTTP/2 stack.** Some libraries are built specifically to reproduce a target browser's JA3/JA4, cipher order, ALPN, and HTTP/2 SETTINGS. The `curl_cffi` family, for example, can present a real Chrome TLS fingerprint rather than the OpenSSL default. When you use one, your JA3 and your `User-Agent` finally tell the same story. * **Run an actual browser.** A real Chrome produces a real Chrome handshake by definition. This is heavier, but for the strictest targets it is the reliable path because every layer stays coherent for free. Whichever you pick, the principle is coherence: the TLS layer, the HTTP/2 layer, the headers, and the client-side `navigator` must all agree on one identity. Our writeup on the [Fingerprint API](https://capzy.ai/fingerprints) covers keeping that agreement from the handshake all the way up to canvas and WebGL. # How defenders collect the hash cheaply Worth understanding why this layer is so popular with defenders: it is nearly free to collect and very hard to forge. The ClientHello arrives before a single byte of your application request. A reverse proxy or CDN edge computes the JA3/JA4 hash during the handshake it was already performing, attaches it as a header for the origin, and moves on. There is no extra round trip, no JavaScript to inject, no challenge to serve. The defender gets a high-signal fingerprint at zero marginal cost, then simply compares it against a table of known-browser hashes. A hash that matches no real browser, or that contradicts the `User-Agent`, is flagged before your request is ever routed. That asymmetry is the whole reason this layer wins so often. Spoofing a header is one line of code on your side. Reproducing a real Chrome ClientHello means matching the cipher order, the extension order, the GREASE values, the supported groups, and the ALPN list, all of which are baked into your TLS library rather than exposed as a setting. Most scrapers never touch it because their HTTP client does not expose it, so they lose at a layer they did not know they were playing on. # A quick self-check Before blaming your headers or your solver, verify what your client actually sends at the handshake. A public JA3/JA4 echo service will show you the hash your client produces and whether it matches a known browser. 1. Send a request from your scraper to a TLS-fingerprint echo endpoint. 2. Read back the JA3/JA4 hash it observed. 3. Compare it to a real Chrome's known hash for the same version. 4. If they differ, your handshake is the block, not your headers. If step 4 shows a mismatch, no amount of header tuning will fix it. You need a browser-accurate client or a real browser. # The IP reputation multiplier TLS coherence and IP reputation compound. A perfect Chrome JA3 from a burned datacenter IP still fails, and a clean residential IP with a Python JA3 still fails. You need both: a browser-accurate handshake and reputation-clean egress from a [Proxies API](https://capzy.ai/proxies). Get either wrong and you lose points at the network layer before a challenge ever appears. # Where Capzy fits Capzy handles the layer most scrapers miss. Our solvers mint tokens from sessions with browser-accurate TLS and HTTP/2 fingerprints, coherent headers, and reputation-clean IPs, so your JA3 and your User-Agent never contradict each other. Browse the [solver catalog](https://capzy.ai/solvers) to see coverage across DataDome, Akamai, PerimeterX, Kasada, and more. If your headers look perfect but you still get blocked, look at the handshake. [Create an account](https://capzy.ai/auth/register) and send your next request through a session that is coherent from the TLS layer up.",
"selftext_html": "<!-- SC_OFF --><div class="md"><h1>TLS and HTTP/2 Fingerprinting (JA3/JA4): The Layer Most Scrapers Miss</h1> <p>Your headers are perfect. Your <code>User-Agent</code> says Chrome 120, your client hints match, your <code>Accept-Language</code> is set. And you still get blocked on the very first request, before any JavaScript runs. The reason is a layer almost every scraper ignores: TLS and HTTP/2 fingerprinting. Before your headers are ever parsed, the way your client negotiates the encrypted connection produces a JA3 or JA4 hash, and if that hash says "Python client" while your header says "Chrome," the contradiction blocks you at the handshake.</p> <p>This is the layer most tutorials skip. Here is how it works and why it matters more than the headers you have been tuning.</p> <h1>What gets fingerprinted in a TLS handshake</h1> <p>When any client opens an HTTPS connection, it sends a <strong>ClientHello</strong> that advertises what it supports. That message is far more revealing than people realize. It contains:</p> <ul> <li><strong>The cipher suite list, in order.</strong> Which encryption algorithms the client offers, and crucially the exact order. Chrome offers a specific ordered list. OpenSSL defaults, which most Python libraries inherit, offer a different one.</li> <li><strong>TLS extensions, in order.</strong> ALPN, supported groups, signature algorithms, key share, and more. Both which extensions are present and their ordering vary by client.</li> <li><strong>Supported elliptic curves and point formats.</strong> The named groups a client will use for key exchange.</li> <li><strong>The TLS version and supported versions.</strong></li> </ul> <p>A JA3 hash is an MD5 of a comma-joined string built from these fields (TLS version, cipher list, extension list, curves, point formats). JA4 is the newer, more structured successor that is harder to spoof and includes ALPN and a sorted-versus-unsorted view of extensions. Either way, the result is a compact fingerprint of your TLS stack.</p> <blockquote> <p>Your JA3/JA4 is decided by your TLS library, not by the headers you set. You can spoof a User-Agent in one line. You cannot spoof a JA3 hash without changing how the handshake itself is built.</p> </blockquote> <h1>Why default HTTP clients betray you</h1> <p>Here is the trap. A default Python <code>requests</code> or <code>httpx</code> call negotiates TLS through the system OpenSSL. Its cipher order, extension set, and curve list produce a JA3 that no browser emits. So a defender sees a request that claims to be Chrome in the <code>User-Agent</code> but has the TLS signature of a scripting language. That mismatch is one of the cheapest, most reliable bot signals in existence, and it fires before your code sends anything else.</p> <pre><code>Your header says: User-Agent: ...Chrome/120... Your JA3 hash says: (a Python/OpenSSL signature no Chrome produces) Defender's conclusion: lying client -> block </code></pre> <p>This is why people report that "adding more headers did nothing." The block was never about headers. It was the handshake.</p> <h1>HTTP/2 fingerprinting</h1> <p>Even after TLS, the story continues. Modern browsers speak HTTP/2, and the way a client sets up an HTTP/2 connection is itself a fingerprint. Defenders look at:</p> <ul> <li><strong>SETTINGS frame values.</strong> The initial header table size, max concurrent streams, initial window size, and the order these settings appear in. Browsers use characteristic values.</li> <li><strong>WINDOW_UPDATE behavior.</strong> How the client manages flow control.</li> <li><strong>Pseudo-header order.</strong> The order of <code>:method</code>, <code>:authority</code>, <code>:scheme</code>, <code>:path</code> in the HEADERS frame. Browsers use a stable order. Many libraries do not, or fall back to HTTP/1.1 entirely, which is itself a tell against a "modern Chrome" claim.</li> <li><strong>Priority and frame ordering.</strong> The overall choreography of frames on the connection.</li> </ul> <p>Combined, the TLS ClientHello and the HTTP/2 setup give a defender a network-layer identity that is very hard to fake from a generic client, and that has to agree with the browser identity you claim on top of it.</p> <h1>ALPN and the small details that leak</h1> <p>One easy-to-miss field is <strong>ALPN</strong> (Application-Layer Protocol Negotiation), the extension where the client says whether it prefers <code>h2</code> (HTTP/2) or <code>http/1.1</code>. A client that advertises <code>http/1.1</code> only, while claiming to be a current Chrome that always offers <code>h2</code>, is another contradiction. GREASE values, the deliberately random extension values Chrome injects to keep the ecosystem flexible, are another. Their presence and pattern are part of the expected Chrome signature, and a client that omits them looks unlike real Chrome.</p> <h1>How to actually fix it</h1> <p>You have two honest options:</p> <ul> <li><strong>Use a client that mimics a real browser's TLS and HTTP/2 stack.</strong> Some libraries are built specifically to reproduce a target browser's JA3/JA4, cipher order, ALPN, and HTTP/2 SETTINGS. The <code>curl_cffi</code> family, for example, can present a real Chrome TLS fingerprint rather than the OpenSSL default. When you use one, your JA3 and your <code>User-Agent</code> finally tell the same story.</li> <li><strong>Run an actual browser.</strong> A real Chrome produces a real Chrome handshake by definition. This is heavier, but for the strictest targets it is the reliable path because every layer stays coherent for free.</li> </ul> <p>Whichever you pick, the principle is coherence: the TLS layer, the HTTP/2 layer, the headers, and the client-side <code>navigator</code> must all agree on one identity. Our writeup on the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> covers keeping that agreement from the handshake all the way up to canvas and WebGL.</p> <h1>How defenders collect the hash cheaply</h1> <p>Worth understanding why this layer is so popular with defenders: it is nearly free to collect and very hard to forge. The ClientHello arrives before a single byte of your application request. A reverse proxy or CDN edge computes the JA3/JA4 hash during the handshake it was already performing, attaches it as a header for the origin, and moves on. There is no extra round trip, no JavaScript to inject, no challenge to serve. The defender gets a high-signal fingerprint at zero marginal cost, then simply compares it against a table of known-browser hashes. A hash that matches no real browser, or that contradicts the <code>User-Agent</code>, is flagged before your request is ever routed.</p> <p>That asymmetry is the whole reason this layer wins so often. Spoofing a header is one line of code on your side. Reproducing a real Chrome ClientHello means matching the cipher order, the extension order, the GREASE values, the supported groups, and the ALPN list, all of which are baked into your TLS library rather than exposed as a setting. Most scrapers never touch it because their HTTP client does not expose it, so they lose at a layer they did not know they were playing on.</p> <h1>A quick self-check</h1> <p>Before blaming your headers or your solver, verify what your client actually sends at the handshake. A public JA3/JA4 echo service will show you the hash your client produces and whether it matches a known browser.</p> <pre><code>1. Send a request from your scraper to a TLS-fingerprint echo endpoint. 2. Read back the JA3/JA4 hash it observed. 3. Compare it to a real Chrome's known hash for the same version. 4. If they differ, your handshake is the block, not your headers. </code></pre> <p>If step 4 shows a mismatch, no amount of header tuning will fix it. You need a browser-accurate client or a real browser.</p> <h1>The IP reputation multiplier</h1> <p>TLS coherence and IP reputation compound. A perfect Chrome JA3 from a burned datacenter IP still fails, and a clean residential IP with a Python JA3 still fails. You need both: a browser-accurate handshake and reputation-clean egress from a <a href="https://capzy.ai/proxies">Proxies API</a>. Get either wrong and you lose points at the network layer before a challenge ever appears.</p> <h1>Where Capzy fits</h1> <p>Capzy handles the layer most scrapers miss. Our solvers mint tokens from sessions with browser-accurate TLS and HTTP/2 fingerprints, coherent headers, and reputation-clean IPs, so your JA3 and your User-Agent never contradict each other. Browse the <a href="https://capzy.ai/solvers">solver catalog</a> to see coverage across DataDome, Akamai, PerimeterX, Kasada, and more.</p> <p>If your headers look perfect but you still get blocked, look at the handshake. <a href="https://capzy.ai/auth/register">Create an account</a> and send your next request through a session that is coherent from the TLS layer up.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/tls-http2-fingerprinting-ja3-ja4",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3i0j/tls_and_http2_fingerprinting_ja3ja4_the_layer/",
"url_overridden_by_dest": "https://capzy.ai/blog/tls-http2-fingerprinting-ja3-ja4",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:56:34+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/g07m7pElin5gWxnSzA1NUqLS1jAkVQSNTN4t262-mHM.jpeg?width=140&height=73&auto=webp&s=9455d3e6c5621e017c23ac0a245c5b9e070d9d3c",
"preview_image": "https://external-preview.redd.it/g07m7pElin5gWxnSzA1NUqLS1jAkVQSNTN4t262-mHM.jpeg?auto=webp&s=825da00db934e16bb7e02754b3a781526e386d32",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3hqc",
"fullname": "t3_1vh3hqc",
"scraped_at": "2026-08-18T09:36:55.069474+00:00",
"title": "Canvas and WebGL Fingerprinting: How Sites Tell Bots Apart",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "If you have ever wondered how a site recognizes the same automated client across sessions even after you clear cookies, canvas and WebGL fingerprinting is a large part of the answer. Both techniques ask the browser to draw something, then read back the exact pixels. Because the pixels depend on your GPU, driver, font stack, and anti-aliasing, the result is a stable id that survives cookie wipes and incognito mode. The short version: fingerprinting does not care what you claim in your user agent. It measures what your machine actually produces when it renders. Bots get caught not because the technique is magic, but because a headless VM renders differently from a real desktop, and that difference is easy to hash and compare. Beating it is about coherence, producing output that matches a real, GPU-backed machine, not about hiding the canvas. # What canvas fingerprinting actually measures A canvas fingerprint works by drawing text and shapes to an offscreen `<canvas>`, then calling `toDataURL()` to serialize the rendered pixels. The same drawing instructions produce slightly different pixel values on different systems because sub-pixel anti-aliasing, font hinting, and rasterization are all implementation-specific. The site hashes that output into an id. function canvasHash() { const c = document.createElement("canvas"); c.width = 240; c.height = 60; const ctx = c.getContext("2d"); ctx.textBaseline = "top"; ctx.font = "16px 'Arial'"; ctx.fillStyle = "#f60"; ctx.fillRect(10, 10, 100, 30); ctx.fillStyle = "#069"; ctx.fillText("Capzy fingerprint \u2601 probe", 12, 14); const data = c.toDataURL(); let h = 0; for (let i = 0; i < data.length; i++) { h = (h * 31 + data.charCodeAt(i)) | 0; } return h; } Two things trip up automation here. The first is a blank or identical canvas: some naive stealth setups block canvas reads or return a constant, which is itself a signal because real browsers never do that. The second is a canvas that renders, but renders like software instead of hardware. That is where WebGL comes in. # WebGL and the renderer string WebGL exposes the graphics pipeline more directly. Through the `WEBGL_debug_renderer_info` extension a site can read `UNMASKED_VENDOR_WEBGL` and `UNMASKED_RENDERER_WEBGL`, which on a real machine return something like "Google Inc. (NVIDIA)" and a specific GPU and driver string. It can also draw a 3D scene and hash the readback pixels, exactly like the 2D case but with more surface area for hardware differences. const gl = document.createElement("canvas").getContext("webgl"); const dbg = gl.getExtension("WEBGL_debug_renderer_info"); const renderer = gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL); // Real desktop: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 ...)" // Headless VM: "Google SwiftShader" or "llvmpipe (LLVM ...)" The renderer string is the classic tell. A headless browser on a server with no GPU falls back to a software rasterizer, and the string comes back as "SwiftShader" or "llvmpipe". No real consumer visiting a retail site is running llvmpipe. When the renderer says software but the user agent claims Chrome on Windows 11, the two disagree and the visitor scores as automated. # Why bots get caught Put the pieces together and the failure modes are consistent: * **Blank or constant canvas.** Blocking `toDataURL` returns nothing or a fixed value, which no real browser does. * **Software renderer.** SwiftShader or llvmpipe betrays a headless server with no GPU. * **Incoherent stack.** A Windows user agent paired with a Linux font-rendering signature, or a mobile claim with a desktop GPU string. * **Frozen fingerprint.** The same canvas hash across thousands of "different" users from one subnet. >Fingerprinting does not read what you claim. It reads what your machine renders. If the two disagree, you are the anomaly, and the fix is coherence, not concealment. None of these is beaten by a single flag. You cannot spoof the renderer string and call it done, because the actual pixel readback still has to match a machine that would report that renderer. The output and the claim have to line up. # What coherent rendering looks like There are two honest ways to pass. The first is to render on a real GPU-backed engine so the canvas and WebGL output are genuinely those of a real machine. That is what a real [Cloud Browser](https://capzy.ai/browser) gives you: you drive a remote Chrome over CDP that runs on hardware producing authentic rendering, instead of hosting a headless VM on your own box. [Capium](https://capzy.ai/capium), our first-party stealth browser you install with `pip install capium`, takes the same approach with additional evasion for the harder targets. The second is to spoof consistently. If you must present a specific device, every signal has to agree: the canvas hash, the WebGL renderer, the fonts, the timezone, and the outbound IP all describe one plausible machine. That coherence is exactly what the [Fingerprint API](https://capzy.ai/fingerprints) is built to produce, generating profiles where the graphics signature matches the rest of the stack instead of being bolted on at random. Pair either approach with a clean IP from the [Proxies API](https://capzy.ai/proxies), because a perfect fingerprint from a flagged datacenter range still stands out. Detection is multi-signal, so the defense has to be too. # Closing Canvas and WebGL fingerprinting are stable, cheap, and hard to fake with a single trick, which is why they show up in almost every serious anti-bot stack. The takeaway is not to fear them but to understand that they measure real rendering. Match a real machine, on real hardware or with a coherent spoof, and the signal that was catching you becomes just another consistent data point. Want to see coherent rendering in practice? [Create a free account](https://capzy.ai/auth/register), drive a page through a real Cloud Browser, and compare its canvas and WebGL output against your current headless setup. For task schemas across supported challenges, see [the solvers page](https://capzy.ai/solvers).",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>If you have ever wondered how a site recognizes the same automated client across sessions even after you clear cookies, canvas and WebGL fingerprinting is a large part of the answer. Both techniques ask the browser to draw something, then read back the exact pixels. Because the pixels depend on your GPU, driver, font stack, and anti-aliasing, the result is a stable id that survives cookie wipes and incognito mode.</p> <p>The short version: fingerprinting does not care what you claim in your user agent. It measures what your machine actually produces when it renders. Bots get caught not because the technique is magic, but because a headless VM renders differently from a real desktop, and that difference is easy to hash and compare. Beating it is about coherence, producing output that matches a real, GPU-backed machine, not about hiding the canvas.</p> <h1>What canvas fingerprinting actually measures</h1> <p>A canvas fingerprint works by drawing text and shapes to an offscreen <code><canvas></code>, then calling <code>toDataURL()</code> to serialize the rendered pixels. The same drawing instructions produce slightly different pixel values on different systems because sub-pixel anti-aliasing, font hinting, and rasterization are all implementation-specific. The site hashes that output into an id.</p> <pre><code>function canvasHash() { const c = document.createElement("canvas"); c.width = 240; c.height = 60; const ctx = c.getContext("2d"); ctx.textBaseline = "top"; ctx.font = "16px 'Arial'"; ctx.fillStyle = "#f60"; ctx.fillRect(10, 10, 100, 30); ctx.fillStyle = "#069"; ctx.fillText("Capzy fingerprint \u2601 probe", 12, 14); const data = c.toDataURL(); let h = 0; for (let i = 0; i < data.length; i++) { h = (h * 31 + data.charCodeAt(i)) | 0; } return h; } </code></pre> <p>Two things trip up automation here. The first is a blank or identical canvas: some naive stealth setups block canvas reads or return a constant, which is itself a signal because real browsers never do that. The second is a canvas that renders, but renders like software instead of hardware. That is where WebGL comes in.</p> <h1>WebGL and the renderer string</h1> <p>WebGL exposes the graphics pipeline more directly. Through the <code>WEBGL_debug_renderer_info</code> extension a site can read <code>UNMASKED_VENDOR_WEBGL</code> and <code>UNMASKED_RENDERER_WEBGL</code>, which on a real machine return something like "Google Inc. (NVIDIA)" and a specific GPU and driver string. It can also draw a 3D scene and hash the readback pixels, exactly like the 2D case but with more surface area for hardware differences.</p> <pre><code>const gl = document.createElement("canvas").getContext("webgl"); const dbg = gl.getExtension("WEBGL_debug_renderer_info"); const renderer = gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL); // Real desktop: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 ...)" // Headless VM: "Google SwiftShader" or "llvmpipe (LLVM ...)" </code></pre> <p>The renderer string is the classic tell. A headless browser on a server with no GPU falls back to a software rasterizer, and the string comes back as "SwiftShader" or "llvmpipe". No real consumer visiting a retail site is running llvmpipe. When the renderer says software but the user agent claims Chrome on Windows 11, the two disagree and the visitor scores as automated.</p> <h1>Why bots get caught</h1> <p>Put the pieces together and the failure modes are consistent:</p> <ul> <li><strong>Blank or constant canvas.</strong> Blocking <code>toDataURL</code> returns nothing or a fixed value, which no real browser does.</li> <li><strong>Software renderer.</strong> SwiftShader or llvmpipe betrays a headless server with no GPU.</li> <li><strong>Incoherent stack.</strong> A Windows user agent paired with a Linux font-rendering signature, or a mobile claim with a desktop GPU string.</li> <li><strong>Frozen fingerprint.</strong> The same canvas hash across thousands of "different" users from one subnet.</li> </ul> <blockquote> <p>Fingerprinting does not read what you claim. It reads what your machine renders. If the two disagree, you are the anomaly, and the fix is coherence, not concealment.</p> </blockquote> <p>None of these is beaten by a single flag. You cannot spoof the renderer string and call it done, because the actual pixel readback still has to match a machine that would report that renderer. The output and the claim have to line up.</p> <h1>What coherent rendering looks like</h1> <p>There are two honest ways to pass. The first is to render on a real GPU-backed engine so the canvas and WebGL output are genuinely those of a real machine. That is what a real <a href="https://capzy.ai/browser">Cloud Browser</a> gives you: you drive a remote Chrome over CDP that runs on hardware producing authentic rendering, instead of hosting a headless VM on your own box. <a href="https://capzy.ai/capium">Capium</a>, our first-party stealth browser you install with <code>pip install capium</code>, takes the same approach with additional evasion for the harder targets.</p> <p>The second is to spoof consistently. If you must present a specific device, every signal has to agree: the canvas hash, the WebGL renderer, the fonts, the timezone, and the outbound IP all describe one plausible machine. That coherence is exactly what the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> is built to produce, generating profiles where the graphics signature matches the rest of the stack instead of being bolted on at random.</p> <p>Pair either approach with a clean IP from the <a href="https://capzy.ai/proxies">Proxies API</a>, because a perfect fingerprint from a flagged datacenter range still stands out. Detection is multi-signal, so the defense has to be too.</p> <h1>Closing</h1> <p>Canvas and WebGL fingerprinting are stable, cheap, and hard to fake with a single trick, which is why they show up in almost every serious anti-bot stack. The takeaway is not to fear them but to understand that they measure real rendering. Match a real machine, on real hardware or with a coherent spoof, and the signal that was catching you becomes just another consistent data point.</p> <p>Want to see coherent rendering in practice? <a href="https://capzy.ai/auth/register">Create a free account</a>, drive a page through a real Cloud Browser, and compare its canvas and WebGL output against your current headless setup. For task schemas across supported challenges, see <a href="https://capzy.ai/solvers">the solvers page</a>.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/canvas-webgl-fingerprinting",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3hqc/canvas_and_webgl_fingerprinting_how_sites_tell/",
"url_overridden_by_dest": "https://capzy.ai/blog/canvas-webgl-fingerprinting",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:56:14+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/zMxP3HaPQtzM7b3NW3x-A-YjTHw1Bl6w-0BuiVW5wmo.jpeg?width=140&height=73&auto=webp&s=2b65222218a4d99cb2c0d1c958cccb21a840d323",
"preview_image": "https://external-preview.redd.it/zMxP3HaPQtzM7b3NW3x-A-YjTHw1Bl6w-0BuiVW5wmo.jpeg?auto=webp&s=86e8d43856c0ff42f981b452d10f62fe7c63db5a",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3he2",
"fullname": "t3_1vh3he2",
"scraped_at": "2026-08-18T09:36:55.069482+00:00",
"title": "How to Bypass CAPTCHAs During Web Scraping Without Getting Blocked",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "# How to Bypass CAPTCHAs During Web Scraping Without Getting Blocked If you want to bypass CAPTCHAs during web scraping without getting blocked, the first thing to accept is that the challenge is rarely the problem on its own. A CAPTCHA is the symptom. The site already flagged your request before it ever painted a puzzle. By the time you see a reCAPTCHA checkbox or a Turnstile widget, the server has looked at your IP reputation, your TLS handshake, your header order, and how fast you clicked through the last three pages. Solving the puzzle is step three of a five-step problem. So the goal is not "beat the CAPTCHA." The goal is to look like a normal visitor long enough that the site either never challenges you, or challenges you rarely and you clear it cleanly. Everything below is in service of that. # Why you got blocked in the first place Most scrapers get flagged for boring reasons. A datacenter IP that a thousand other bots already burned. A `User-Agent` that says Chrome 120 while the TLS fingerprint says Python. Requests firing every 40 milliseconds with no jitter. Cookies that never persist, so every hit looks like a brand-new visitor with no history. Here is a quick mental checklist before you touch any solver: * Is your IP residential or mobile, and is it sticky for the session? * Does your TLS and HTTP/2 fingerprint match the browser you claim to be? * Are you reusing cookies across a session, or throwing them away every request? * Is your request timing varied, or is it a metronome? Fix those and a large share of challenges disappear on their own. That is the cheapest CAPTCHA "bypass" there is: never trigger one. # The three layers that actually keep data flowing A working scraper stack has three cooperating pieces, and you want them talking to each other rather than bolted on as afterthoughts. **Layer one is the proxy.** You need clean egress IPs with good reputation, and for anything session-based you need the IP to stay sticky so your cookies and your source address agree. Capzy's [Proxies API](https://capzy.ai/proxies) gives you rotating and sticky options so a session that starts on one address finishes on it too. Rotating a fresh IP mid-session is a classic self-inflicted block: the site sees a logged-in cookie suddenly arrive from a new country and quietly flags it. **Layer two is the solver.** When a challenge does appear, you hand it off to something that returns a token. Capzy's [Solver API](https://capzy.ai/solvers) takes the site key and page URL and gives you back a response token you inject into the form. This covers the widget families you will actually hit in the wild, including reCAPTCHA, hCaptcha, and Turnstile. **Layer three, when you need it, is a real browser.** Some sites gate their content behind JavaScript that only runs correctly in an actual rendering engine, and some anti-bot systems score how the page behaves after load. For those, a headless script is not enough and you want Capzy's [Cloud Browser](https://capzy.ai/browser), a real Chrome you drive over CDP. >The trap most teams fall into: they buy a solver, wire it in, and still get blocked because their proxies are dirty and their sessions leak. The solver was never the weak link. Fix the identity first, then the solver has an easy job. # A concrete flow Say you are scraping a product catalog that throws a Turnstile challenge on the search endpoint. The pattern looks like this. You make your request through a sticky residential IP. If the response comes back clean, great, you parse and move on. If it comes back with a challenge, you extract the site key from the page, send it to the solver, get a token, replay the request with the token attached, and continue on the same IP and cookie jar. Here is the solver hand-off in plain Python: import requests API_KEY = "your_capzy_key" # 1. Create the solve task with the site key you scraped from the page task = requests.post( "https://api.capzy.ai/createTask", json={ "clientKey": API_KEY, "task": { "type": "TurnstileTaskProxyless", "websiteURL": "https://shop.example.com/search", "websiteKey": "0x4AAAAAAABkMYinukE8nzYS", }, }, timeout=30, ).json() task_id = task["taskId"] # 2. Poll for the token import time while True: result = requests.post( "https://api.capzy.ai/getTaskResult", json={"clientKey": API_KEY, "taskId": task_id}, timeout=30, ).json() if result.get("status") == "ready": token = result["solution"]["token"] break time.sleep(2) # 3. Replay the original request WITH the token, on the SAME session session = requests.Session() session.proxies = {"https": "http://user:pass@sticky.capzy.ai:8000"} resp = session.post( "https://shop.example.com/search", data={"q": "wireless earbuds", "cf-turnstile-response": token}, ) print(resp.status_code) The important detail is step three. The token is bound to the site and often to the session context. If you solve on one IP and replay on another, the site may reject a perfectly valid token because the context no longer matches. Keep the solve, the proxy, and the replay on one coherent identity. One more thing that trips people up: cache tokens carefully or not at all. A response token has a short lifetime, often around two minutes, and many are single-use. Reusing a stale token feels like a free optimization until the site starts silently rejecting them and your solve rate craters for no visible reason. If you are solving in a loop, solve fresh per request unless you have confirmed the token type is safe to reuse for that specific widget and site. # Rate, timing, and the human-shaped curve Even with clean IPs and a working solver, you can burn a good setup with bad pacing. A real person does not request 200 pages in 90 seconds. Add jitter to your delays, respect the natural rhythm of the site, and back off when you see the first sign of friction. A single `429` is a warning; three in a row is you being throttled toward a block. When you do get challenged repeatedly on the same target, that is usually IP reputation talking, not the solver failing. Rotate to a fresh clean address, slow down, and let the previous IP cool off. Solve rate is a product of the whole pipeline, not one component. # When to let a managed API carry it Wiring proxies, solving, retries, and session state together is real work, and it breaks in annoying ways at 2 a.m. If you would rather send a URL and get parsed data back, Capzy's [Web Scraper API](https://capzy.ai/web-scraper) bundles the proxy rotation, the challenge handling, and optional JavaScript rendering into one `/scrape` call. You point it at a page, it returns the content, and the CAPTCHA layer is handled for you. The honest summary: you rarely "bypass" a CAPTCHA by outsmarting the puzzle. You bypass it by not looking like a bot, and by having a clean token ready for the times the site challenges you anyway. Get the proxy identity right, keep sessions coherent, pace like a human, and hand the puzzle to a solver only when one shows up. Ready to stop fighting blocks? [Create a free Capzy account](https://capzy.ai/auth/register) and route your next scrape through clean proxies with solving built in.",
"selftext_html": "<!-- SC_OFF --><div class="md"><h1>How to Bypass CAPTCHAs During Web Scraping Without Getting Blocked</h1> <p>If you want to bypass CAPTCHAs during web scraping without getting blocked, the first thing to accept is that the challenge is rarely the problem on its own. A CAPTCHA is the symptom. The site already flagged your request before it ever painted a puzzle. By the time you see a reCAPTCHA checkbox or a Turnstile widget, the server has looked at your IP reputation, your TLS handshake, your header order, and how fast you clicked through the last three pages. Solving the puzzle is step three of a five-step problem.</p> <p>So the goal is not "beat the CAPTCHA." The goal is to look like a normal visitor long enough that the site either never challenges you, or challenges you rarely and you clear it cleanly. Everything below is in service of that.</p> <h1>Why you got blocked in the first place</h1> <p>Most scrapers get flagged for boring reasons. A datacenter IP that a thousand other bots already burned. A <code>User-Agent</code> that says Chrome 120 while the TLS fingerprint says Python. Requests firing every 40 milliseconds with no jitter. Cookies that never persist, so every hit looks like a brand-new visitor with no history.</p> <p>Here is a quick mental checklist before you touch any solver:</p> <ul> <li>Is your IP residential or mobile, and is it sticky for the session?</li> <li>Does your TLS and HTTP/2 fingerprint match the browser you claim to be?</li> <li>Are you reusing cookies across a session, or throwing them away every request?</li> <li>Is your request timing varied, or is it a metronome?</li> </ul> <p>Fix those and a large share of challenges disappear on their own. That is the cheapest CAPTCHA "bypass" there is: never trigger one.</p> <h1>The three layers that actually keep data flowing</h1> <p>A working scraper stack has three cooperating pieces, and you want them talking to each other rather than bolted on as afterthoughts.</p> <p><strong>Layer one is the proxy.</strong> You need clean egress IPs with good reputation, and for anything session-based you need the IP to stay sticky so your cookies and your source address agree. Capzy's <a href="https://capzy.ai/proxies">Proxies API</a> gives you rotating and sticky options so a session that starts on one address finishes on it too. Rotating a fresh IP mid-session is a classic self-inflicted block: the site sees a logged-in cookie suddenly arrive from a new country and quietly flags it.</p> <p><strong>Layer two is the solver.</strong> When a challenge does appear, you hand it off to something that returns a token. Capzy's <a href="https://capzy.ai/solvers">Solver API</a> takes the site key and page URL and gives you back a response token you inject into the form. This covers the widget families you will actually hit in the wild, including reCAPTCHA, hCaptcha, and Turnstile.</p> <p><strong>Layer three, when you need it, is a real browser.</strong> Some sites gate their content behind JavaScript that only runs correctly in an actual rendering engine, and some anti-bot systems score how the page behaves after load. For those, a headless script is not enough and you want Capzy's <a href="https://capzy.ai/browser">Cloud Browser</a>, a real Chrome you drive over CDP.</p> <blockquote> <p>The trap most teams fall into: they buy a solver, wire it in, and still get blocked because their proxies are dirty and their sessions leak. The solver was never the weak link. Fix the identity first, then the solver has an easy job.</p> </blockquote> <h1>A concrete flow</h1> <p>Say you are scraping a product catalog that throws a Turnstile challenge on the search endpoint. The pattern looks like this. You make your request through a sticky residential IP. If the response comes back clean, great, you parse and move on. If it comes back with a challenge, you extract the site key from the page, send it to the solver, get a token, replay the request with the token attached, and continue on the same IP and cookie jar.</p> <p>Here is the solver hand-off in plain Python:</p> <pre><code>import requests API_KEY = "your_capzy_key" # 1. Create the solve task with the site key you scraped from the page task = requests.post( "https://api.capzy.ai/createTask", json={ "clientKey": API_KEY, "task": { "type": "TurnstileTaskProxyless", "websiteURL": "https://shop.example.com/search", "websiteKey": "0x4AAAAAAABkMYinukE8nzYS", }, }, timeout=30, ).json() task_id = task["taskId"] # 2. Poll for the token import time while True: result = requests.post( "https://api.capzy.ai/getTaskResult", json={"clientKey": API_KEY, "taskId": task_id}, timeout=30, ).json() if result.get("status") == "ready": token = result["solution"]["token"] break time.sleep(2) # 3. Replay the original request WITH the token, on the SAME session session = requests.Session() session.proxies = {"https": "http://user:pass@sticky.capzy.ai:8000"} resp = session.post( "https://shop.example.com/search", data={"q": "wireless earbuds", "cf-turnstile-response": token}, ) print(resp.status_code) </code></pre> <p>The important detail is step three. The token is bound to the site and often to the session context. If you solve on one IP and replay on another, the site may reject a perfectly valid token because the context no longer matches. Keep the solve, the proxy, and the replay on one coherent identity.</p> <p>One more thing that trips people up: cache tokens carefully or not at all. A response token has a short lifetime, often around two minutes, and many are single-use. Reusing a stale token feels like a free optimization until the site starts silently rejecting them and your solve rate craters for no visible reason. If you are solving in a loop, solve fresh per request unless you have confirmed the token type is safe to reuse for that specific widget and site.</p> <h1>Rate, timing, and the human-shaped curve</h1> <p>Even with clean IPs and a working solver, you can burn a good setup with bad pacing. A real person does not request 200 pages in 90 seconds. Add jitter to your delays, respect the natural rhythm of the site, and back off when you see the first sign of friction. A single <code>429</code> is a warning; three in a row is you being throttled toward a block.</p> <p>When you do get challenged repeatedly on the same target, that is usually IP reputation talking, not the solver failing. Rotate to a fresh clean address, slow down, and let the previous IP cool off. Solve rate is a product of the whole pipeline, not one component.</p> <h1>When to let a managed API carry it</h1> <p>Wiring proxies, solving, retries, and session state together is real work, and it breaks in annoying ways at 2 a.m. If you would rather send a URL and get parsed data back, Capzy's <a href="https://capzy.ai/web-scraper">Web Scraper API</a> bundles the proxy rotation, the challenge handling, and optional JavaScript rendering into one <code>/scrape</code> call. You point it at a page, it returns the content, and the CAPTCHA layer is handled for you.</p> <p>The honest summary: you rarely "bypass" a CAPTCHA by outsmarting the puzzle. You bypass it by not looking like a bot, and by having a clean token ready for the times the site challenges you anyway. Get the proxy identity right, keep sessions coherent, pace like a human, and hand the puzzle to a solver only when one shows up.</p> <p>Ready to stop fighting blocks? <a href="https://capzy.ai/auth/register">Create a free Capzy account</a> and route your next scrape through clean proxies with solving built in.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/bypass-captcha-web-scraping-without-getting-blocked",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3he2/how_to_bypass_captchas_during_web_scraping/",
"url_overridden_by_dest": "https://capzy.ai/blog/bypass-captcha-web-scraping-without-getting-blocked",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:55:50+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/wIvWZYPWdcgsO43O4t-PZydYzvUHDVg8BmQMJrZiX_I.jpeg?width=140&height=73&auto=webp&s=a9e00ead37d5d7503f547bf9ded93a78b0270ecd",
"preview_image": "https://external-preview.redd.it/wIvWZYPWdcgsO43O4t-PZydYzvUHDVg8BmQMJrZiX_I.jpeg?auto=webp&s=3711e8ddc367d8b92d2d21ead90b398b7bcb7290",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3h0y",
"fullname": "t3_1vh3h0y",
"scraped_at": "2026-08-18T09:36:55.069488+00:00",
"title": "How Sites Detect Headless Browsers (and How Solvers Avoid It)",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "If your automation works locally in a headed browser but gets blocked the moment you run it headless on a server, you are almost certainly tripping one of a dozen well-known detection probes. Headless browsers leak. They ship with defaults and missing objects that a real, user-driven Chrome never has, and anti-bot scripts check for those tells within milliseconds of page load. The core answer up front: sites do not need to see your face to know you are automated. They read `navigator.webdriver`, count your plugins, inspect your user agent, and probe for CDP artifacts. Any one of those can flip a visitor from human to bot. Avoiding detection is not about patching a single property, it is about running a real, non-headless engine whose signals are coherent from top to bottom. # The classic JavaScript tells Most headless detection is a handful of property reads that a script runs immediately. Here is a probe that checks several at once: function headlessSignals() { return { webdriver: navigator.webdriver === true, noPlugins: navigator.plugins.length === 0, noLanguages: !navigator.languages || navigator.languages.length === 0, headlessUA: /HeadlessChrome/.test(navigator.userAgent), noChrome: typeof window.chrome === "undefined", weirdViewport: window.outerWidth === 0 || window.outerHeight === 0, }; } // Any `true` here is a point against you. Walk through what each one means: * `navigator.webdriver === true`**.** The spec-defined automation flag. Set by default under automation. A real browser reports `false`. * **Empty** `navigator.plugins` **and** `navigator.languages`**.** Headless Chrome historically ships with zero plugins and, in some configs, an empty language list. Real users have both populated. * `HeadlessChrome` **in the user agent.** The default headless UA literally announces itself. Anyone forgetting to override it is caught instantly. * **Missing** `window.chrome`**.** Real Chrome exposes a [`window.chrome`](http://window.chrome) object with runtime and app members. Its absence is a strong tell. # The subtler probes The obvious flags are easy to patch, so serious anti-bot scripts go deeper. The permissions API is a favorite: a script queries notification permission and cross-checks it against `Notification.permission`. In some headless states these disagree, or `Notification.permission` reads "denied" while the permissions query says "prompt", which no normal browser produces. Window and screen geometry is another. Headless sessions often report a viewport that does not match a real display, or `screen.availWidth` values that never occur on consumer hardware. Scripts also time the rendering of hidden elements and measure whether the tab behaves as if it is actually visible. Then there are CDP artifacts. Driving a browser over the Chrome DevTools Protocol can leave observable traces, and specialized scripts probe for them by, for example, checking whether certain error stack traces or serialization behaviors match a session under the protocol. These are harder to fake because they are side effects of how the browser is being controlled, not just properties you can overwrite. Fonts are another quiet channel. A script measures the rendered width of a string in a list of fonts and infers which are installed. A stock headless server image has a sparse, telltale font set that no consumer desktop matches, and that font list has to agree with the platform your user agent claims. A Windows user agent with a Linux font footprint is a contradiction a scoring engine will happily record. # Why patching one property is not enough The temptation is to override `navigator.webdriver`, spoof the user agent, and inject a fake `window.chrome`. That handles the first-pass probes, but modern detection is a scoring system, not a single gate. It collects dozens of signals and looks for internal contradictions. >A headless browser with fifty patched properties and one forgotten permissions inconsistency still scores as a bot. Coherence across every signal is the bar, and manually patching your way there is a losing maintenance race. The moment you patch the language list but forget it should match the `Accept-Language` header your proxy sends, you have created a new contradiction. Detection wins by finding the seam, and a headless browser has many seams. # The real defense: a real engine The durable fix is to not be headless at all. Run a genuine, user-mode Chrome whose signals are real because the browser really is that browser. Our [Cloud Browser](https://capzy.ai/browser) is exactly this: a remote Chrome you drive over CDP that runs headed on real infrastructure, so `navigator.webdriver` is false, plugins and languages are populated, [`window.chrome`](http://window.chrome) exists, and the geometry is a real display, because it is one. You do not host a fragile stealth VM on your own PC. For the hardest targets, [Capium](https://capzy.ai/capium) (`pip install capium`) is our first-party stealth browser that hardens these signals further and is built specifically to avoid the CDP and geometry tells. Layer a coherent profile from the [Fingerprint API](https://capzy.ai/fingerprints) so the user agent, languages, and headers all agree, and route through a clean IP from the [Proxies API](https://capzy.ai/proxies) so the network layer does not contradict the browser layer. This is also how good CAPTCHA solving works under the hood. Tokens minted from real, non-headless browser sessions pass because the session itself is clean. You can see the supported task types on [the solvers page](https://capzy.ai/solvers). # Closing Headless detection is not one clever check, it is a battery of small ones that add up. You can spend forever patching properties and still lose to the one you missed. The reliable path is a real engine that produces real signals, wrapped in a coherent fingerprint and a clean IP. When every layer tells the same true story, there is no seam left to find. Ready to stop losing to the headless flag? [Create a free account](https://capzy.ai/auth/register) and drive your next job through a real Cloud Browser instead of a headless VM.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>If your automation works locally in a headed browser but gets blocked the moment you run it headless on a server, you are almost certainly tripping one of a dozen well-known detection probes. Headless browsers leak. They ship with defaults and missing objects that a real, user-driven Chrome never has, and anti-bot scripts check for those tells within milliseconds of page load.</p> <p>The core answer up front: sites do not need to see your face to know you are automated. They read <code>navigator.webdriver</code>, count your plugins, inspect your user agent, and probe for CDP artifacts. Any one of those can flip a visitor from human to bot. Avoiding detection is not about patching a single property, it is about running a real, non-headless engine whose signals are coherent from top to bottom.</p> <h1>The classic JavaScript tells</h1> <p>Most headless detection is a handful of property reads that a script runs immediately. Here is a probe that checks several at once:</p> <pre><code>function headlessSignals() { return { webdriver: navigator.webdriver === true, noPlugins: navigator.plugins.length === 0, noLanguages: !navigator.languages || navigator.languages.length === 0, headlessUA: /HeadlessChrome/.test(navigator.userAgent), noChrome: typeof window.chrome === "undefined", weirdViewport: window.outerWidth === 0 || window.outerHeight === 0, }; } // Any `true` here is a point against you. </code></pre> <p>Walk through what each one means:</p> <ul> <li><code>navigator.webdriver === true</code><strong>.</strong> The spec-defined automation flag. Set by default under automation. A real browser reports <code>false</code>.</li> <li><strong>Empty</strong> <code>navigator.plugins</code> <strong>and</strong> <code>navigator.languages</code><strong>.</strong> Headless Chrome historically ships with zero plugins and, in some configs, an empty language list. Real users have both populated.</li> <li><code>HeadlessChrome</code> <strong>in the user agent.</strong> The default headless UA literally announces itself. Anyone forgetting to override it is caught instantly.</li> <li><strong>Missing</strong> <code>window.chrome</code><strong>.</strong> Real Chrome exposes a <a href="http://window.chrome"><code>window.chrome</code></a> object with runtime and app members. Its absence is a strong tell.</li> </ul> <h1>The subtler probes</h1> <p>The obvious flags are easy to patch, so serious anti-bot scripts go deeper. The permissions API is a favorite: a script queries notification permission and cross-checks it against <code>Notification.permission</code>. In some headless states these disagree, or <code>Notification.permission</code> reads "denied" while the permissions query says "prompt", which no normal browser produces.</p> <p>Window and screen geometry is another. Headless sessions often report a viewport that does not match a real display, or <code>screen.availWidth</code> values that never occur on consumer hardware. Scripts also time the rendering of hidden elements and measure whether the tab behaves as if it is actually visible.</p> <p>Then there are CDP artifacts. Driving a browser over the Chrome DevTools Protocol can leave observable traces, and specialized scripts probe for them by, for example, checking whether certain error stack traces or serialization behaviors match a session under the protocol. These are harder to fake because they are side effects of how the browser is being controlled, not just properties you can overwrite.</p> <p>Fonts are another quiet channel. A script measures the rendered width of a string in a list of fonts and infers which are installed. A stock headless server image has a sparse, telltale font set that no consumer desktop matches, and that font list has to agree with the platform your user agent claims. A Windows user agent with a Linux font footprint is a contradiction a scoring engine will happily record.</p> <h1>Why patching one property is not enough</h1> <p>The temptation is to override <code>navigator.webdriver</code>, spoof the user agent, and inject a fake <code>window.chrome</code>. That handles the first-pass probes, but modern detection is a scoring system, not a single gate. It collects dozens of signals and looks for internal contradictions.</p> <blockquote> <p>A headless browser with fifty patched properties and one forgotten permissions inconsistency still scores as a bot. Coherence across every signal is the bar, and manually patching your way there is a losing maintenance race.</p> </blockquote> <p>The moment you patch the language list but forget it should match the <code>Accept-Language</code> header your proxy sends, you have created a new contradiction. Detection wins by finding the seam, and a headless browser has many seams.</p> <h1>The real defense: a real engine</h1> <p>The durable fix is to not be headless at all. Run a genuine, user-mode Chrome whose signals are real because the browser really is that browser. Our <a href="https://capzy.ai/browser">Cloud Browser</a> is exactly this: a remote Chrome you drive over CDP that runs headed on real infrastructure, so <code>navigator.webdriver</code> is false, plugins and languages are populated, <a href="http://window.chrome"><code>window.chrome</code></a> exists, and the geometry is a real display, because it is one. You do not host a fragile stealth VM on your own PC.</p> <p>For the hardest targets, <a href="https://capzy.ai/capium">Capium</a> (<code>pip install capium</code>) is our first-party stealth browser that hardens these signals further and is built specifically to avoid the CDP and geometry tells. Layer a coherent profile from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a> so the user agent, languages, and headers all agree, and route through a clean IP from the <a href="https://capzy.ai/proxies">Proxies API</a> so the network layer does not contradict the browser layer.</p> <p>This is also how good CAPTCHA solving works under the hood. Tokens minted from real, non-headless browser sessions pass because the session itself is clean. You can see the supported task types on <a href="https://capzy.ai/solvers">the solvers page</a>.</p> <h1>Closing</h1> <p>Headless detection is not one clever check, it is a battery of small ones that add up. You can spend forever patching properties and still lose to the one you missed. The reliable path is a real engine that produces real signals, wrapped in a coherent fingerprint and a clean IP. When every layer tells the same true story, there is no seam left to find.</p> <p>Ready to stop losing to the headless flag? <a href="https://capzy.ai/auth/register">Create a free account</a> and drive your next job through a real Cloud Browser instead of a headless VM.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/detecting-headless-browsers",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3h0y/how_sites_detect_headless_browsers_and_how/",
"url_overridden_by_dest": "https://capzy.ai/blog/detecting-headless-browsers",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:55:24+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/5JMtIN5-T1nIfdrWrnW-fn1XErUzI8A80vwW8amJ0B4.jpeg?width=140&height=73&auto=webp&s=9061900202489ada5b2e57ead69ea268ac42f6fd",
"preview_image": "https://external-preview.redd.it/5JMtIN5-T1nIfdrWrnW-fn1XErUzI8A80vwW8amJ0B4.jpeg?auto=webp&s=a6c99bdef1d773d1993698e65fbd0f4ba42d0590",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3giv",
"fullname": "t3_1vh3giv",
"scraped_at": "2026-08-18T09:36:55.069496+00:00",
"title": "Building a Resilient Scraper: Retries, Backoff, and Session Management",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "# Building a Resilient Scraper: Retries, Backoff, and Session Management Building a resilient scraper is less about clever parsing and more about how you handle the request that fails. Any script can pull a page when the network is calm, the target is happy, and the proxy is clean. The engineering shows up when a request times out at 30 seconds, when the site returns a `503` for the next four minutes, or when a session that was logged in ten pages ago suddenly gets a challenge. A resilient scraper expects all of that and keeps going. This is a practical walk through the three things that separate a toy scraper from one that runs unattended for weeks: retries that know what they are retrying, backoff that does not hammer a struggling server, and session management that keeps your identity coherent. # Not every failure deserves a retry The first mistake I see is retrying blindly. Someone wraps the whole request in a loop that runs five times and calls it done. That is worse than no retry, because it turns one transient blip into five requests against a server that may already be rate-limiting you. Sort your failures into buckets first: * **Retry immediately or after short backoff:** `429`, `502`, `503`, `504`, connection resets, read timeouts. These are usually transient. The server is busy or the proxy hiccupped. * **Retry with a new identity:** `403` and repeated CAPTCHA challenges. Retrying on the same IP will not help. You need a fresh proxy and possibly a solved token. * **Do not retry:** `404`, `401` when your credentials are simply wrong, `400` for a malformed request. Retrying a `404` five times just wastes budget. Fix the input instead. Encoding that logic pays off fast. A scraper that retries a `404` is doing five times the work for a page that will never exist. # Exponential backoff, with jitter When you do retry, do not retry on a fixed schedule. If a server returns `503` and you retry after exactly 2 seconds every time, and so do the other 50 workers you have running, you create a thundering herd that all hit the recovering server at the same instant. That is how you turn a brief outage into a longer one. Exponential backoff with jitter fixes this. Wait 1 second, then 2, then 4, then 8, and add a random fraction so your workers spread out instead of synchronizing. import random import time import requests RETRYABLE = {429, 502, 503, 504} def fetch(url, session, max_attempts=5): for attempt in range(max_attempts): try: resp = session.get(url, timeout=20) except (requests.ConnectionError, requests.Timeout): resp = None if resp is not None and resp.status_code == 200: return resp if resp is not None and resp.status_code not in RETRYABLE: # 404, 401, 400 etc. Do not waste retries. resp.raise_for_status() # Exponential backoff: 1, 2, 4, 8 ... plus jitter wait = (2 ** attempt) + random.uniform(0, 1) # Honor Retry-After when the server tells you how long to wait if resp is not None and "Retry-After" in resp.headers: wait = max(wait, int(resp.headers["Retry-After"])) time.sleep(wait) raise RuntimeError(f"Gave up on {url} after {max_attempts} attempts") Two details worth calling out. First, cap your attempts. Infinite retries are how you discover a runaway process burning proxy bandwidth over a weekend. Five attempts with exponential backoff already stretches over roughly 30 seconds of waiting, which covers most transient failures. Second, honor `Retry-After` when the server sends it. The site is literally telling you how long to wait. Listen. >A good rule of thumb: if your retry logic cannot explain, in one sentence, why it is retrying this specific status code, it is retrying too much. Retries are a scalpel, not a hammer. # Session management is where identity lives Retries handle the network. Session management handles who you appear to be. This is the part people skip, and it is the part that gets them blocked. A session is the bundle of things that make you look like one continuous visitor: your cookies, your source IP, your headers. The cardinal rule is that these must stay consistent together. If you start a session logged in from a sticky residential IP and then rotate to a fresh IP on the next request, the site sees a logged-in cookie arrive from a new address and flags it. You broke your own disguise. For anything stateful, use sticky proxies so one session keeps one egress address for its whole life. Capzy's [Proxies API](https://capzy.ai/proxies) supports sticky sessions for exactly this reason. Bind a `requests.Session` to a sticky proxy, keep the cookie jar alive, and let the identity persist across the whole crawl of that account or that flow. session = requests.Session() session.proxies = {"https": "http://user:session-abc123@sticky.capzy.ai:8000"} session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept-Language": "en-US,en;q=0.9", }) # Cookies now persist for the life of this session object, # and every request leaves from the same sticky IP. When a session does get challenged despite all this, that is your signal to hand off to a solver. Capzy's [Solver API](https://capzy.ai/solvers) returns a token you inject and replay on the same session, so you clear the challenge without abandoning the identity you built up. # Rotate on the right boundary Here is the balance. You want fresh identities often enough that no single IP gets hot, but you must not rotate mid-session and break coherence. The right boundary to rotate on is the *task*, not the *request*. One account scrape, one flow, one logical unit of work gets one sticky session. When that unit is done, drop it, take a fresh sticky IP, and start the next. Rotate between tasks, stay sticky within them. If a task hits a hard block, do not keep grinding on the poisoned IP. Retire it, cool it off, and give the retry a genuinely fresh identity. This is the "retry with a new identity" bucket from earlier, and it only works if your proxy layer can actually hand you a clean one on demand. # Putting it together A resilient scraper is a small state machine. Classify the failure. Back off with jitter if it is transient. Swap identity if it is a block. Give up cleanly if the resource is gone. Keep cookies and IP married for the life of a task, and rotate on task boundaries. None of these pieces is exotic on its own; the resilience comes from having all of them and letting them cooperate. If you would rather not hand-roll the retry loop and the sticky-session plumbing, Capzy's [Web Scraper API](https://capzy.ai/web-scraper) folds retries, backoff, proxy rotation, and challenge handling into a single call, so a transient `503` or a surprise CAPTCHA is handled before the response ever reaches you. Want to build on infrastructure that handles the failure paths for you? [Sign up for Capzy](https://capzy.ai/auth/register) and get a scraper that stays up when the target does not.",
"selftext_html": "<!-- SC_OFF --><div class="md"><h1>Building a Resilient Scraper: Retries, Backoff, and Session Management</h1> <p>Building a resilient scraper is less about clever parsing and more about how you handle the request that fails. Any script can pull a page when the network is calm, the target is happy, and the proxy is clean. The engineering shows up when a request times out at 30 seconds, when the site returns a <code>503</code> for the next four minutes, or when a session that was logged in ten pages ago suddenly gets a challenge. A resilient scraper expects all of that and keeps going.</p> <p>This is a practical walk through the three things that separate a toy scraper from one that runs unattended for weeks: retries that know what they are retrying, backoff that does not hammer a struggling server, and session management that keeps your identity coherent.</p> <h1>Not every failure deserves a retry</h1> <p>The first mistake I see is retrying blindly. Someone wraps the whole request in a loop that runs five times and calls it done. That is worse than no retry, because it turns one transient blip into five requests against a server that may already be rate-limiting you.</p> <p>Sort your failures into buckets first:</p> <ul> <li><strong>Retry immediately or after short backoff:</strong> <code>429</code>, <code>502</code>, <code>503</code>, <code>504</code>, connection resets, read timeouts. These are usually transient. The server is busy or the proxy hiccupped.</li> <li><strong>Retry with a new identity:</strong> <code>403</code> and repeated CAPTCHA challenges. Retrying on the same IP will not help. You need a fresh proxy and possibly a solved token.</li> <li><strong>Do not retry:</strong> <code>404</code>, <code>401</code> when your credentials are simply wrong, <code>400</code> for a malformed request. Retrying a <code>404</code> five times just wastes budget. Fix the input instead.</li> </ul> <p>Encoding that logic pays off fast. A scraper that retries a <code>404</code> is doing five times the work for a page that will never exist.</p> <h1>Exponential backoff, with jitter</h1> <p>When you do retry, do not retry on a fixed schedule. If a server returns <code>503</code> and you retry after exactly 2 seconds every time, and so do the other 50 workers you have running, you create a thundering herd that all hit the recovering server at the same instant. That is how you turn a brief outage into a longer one.</p> <p>Exponential backoff with jitter fixes this. Wait 1 second, then 2, then 4, then 8, and add a random fraction so your workers spread out instead of synchronizing.</p> <pre><code>import random import time import requests RETRYABLE = {429, 502, 503, 504} def fetch(url, session, max_attempts=5): for attempt in range(max_attempts): try: resp = session.get(url, timeout=20) except (requests.ConnectionError, requests.Timeout): resp = None if resp is not None and resp.status_code == 200: return resp if resp is not None and resp.status_code not in RETRYABLE: # 404, 401, 400 etc. Do not waste retries. resp.raise_for_status() # Exponential backoff: 1, 2, 4, 8 ... plus jitter wait = (2 ** attempt) + random.uniform(0, 1) # Honor Retry-After when the server tells you how long to wait if resp is not None and "Retry-After" in resp.headers: wait = max(wait, int(resp.headers["Retry-After"])) time.sleep(wait) raise RuntimeError(f"Gave up on {url} after {max_attempts} attempts") </code></pre> <p>Two details worth calling out. First, cap your attempts. Infinite retries are how you discover a runaway process burning proxy bandwidth over a weekend. Five attempts with exponential backoff already stretches over roughly 30 seconds of waiting, which covers most transient failures. Second, honor <code>Retry-After</code> when the server sends it. The site is literally telling you how long to wait. Listen.</p> <blockquote> <p>A good rule of thumb: if your retry logic cannot explain, in one sentence, why it is retrying this specific status code, it is retrying too much. Retries are a scalpel, not a hammer.</p> </blockquote> <h1>Session management is where identity lives</h1> <p>Retries handle the network. Session management handles who you appear to be. This is the part people skip, and it is the part that gets them blocked.</p> <p>A session is the bundle of things that make you look like one continuous visitor: your cookies, your source IP, your headers. The cardinal rule is that these must stay consistent together. If you start a session logged in from a sticky residential IP and then rotate to a fresh IP on the next request, the site sees a logged-in cookie arrive from a new address and flags it. You broke your own disguise.</p> <p>For anything stateful, use sticky proxies so one session keeps one egress address for its whole life. Capzy's <a href="https://capzy.ai/proxies">Proxies API</a> supports sticky sessions for exactly this reason. Bind a <code>requests.Session</code> to a sticky proxy, keep the cookie jar alive, and let the identity persist across the whole crawl of that account or that flow.</p> <pre><code>session = requests.Session() session.proxies = {"https": "http://user:session-abc123@sticky.capzy.ai:8000"} session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept-Language": "en-US,en;q=0.9", }) # Cookies now persist for the life of this session object, # and every request leaves from the same sticky IP. </code></pre> <p>When a session does get challenged despite all this, that is your signal to hand off to a solver. Capzy's <a href="https://capzy.ai/solvers">Solver API</a> returns a token you inject and replay on the same session, so you clear the challenge without abandoning the identity you built up.</p> <h1>Rotate on the right boundary</h1> <p>Here is the balance. You want fresh identities often enough that no single IP gets hot, but you must not rotate mid-session and break coherence. The right boundary to rotate on is the <em>task</em>, not the <em>request</em>. One account scrape, one flow, one logical unit of work gets one sticky session. When that unit is done, drop it, take a fresh sticky IP, and start the next. Rotate between tasks, stay sticky within them.</p> <p>If a task hits a hard block, do not keep grinding on the poisoned IP. Retire it, cool it off, and give the retry a genuinely fresh identity. This is the "retry with a new identity" bucket from earlier, and it only works if your proxy layer can actually hand you a clean one on demand.</p> <h1>Putting it together</h1> <p>A resilient scraper is a small state machine. Classify the failure. Back off with jitter if it is transient. Swap identity if it is a block. Give up cleanly if the resource is gone. Keep cookies and IP married for the life of a task, and rotate on task boundaries. None of these pieces is exotic on its own; the resilience comes from having all of them and letting them cooperate.</p> <p>If you would rather not hand-roll the retry loop and the sticky-session plumbing, Capzy's <a href="https://capzy.ai/web-scraper">Web Scraper API</a> folds retries, backoff, proxy rotation, and challenge handling into a single call, so a transient <code>503</code> or a surprise CAPTCHA is handled before the response ever reaches you.</p> <p>Want to build on infrastructure that handles the failure paths for you? <a href="https://capzy.ai/auth/register">Sign up for Capzy</a> and get a scraper that stays up when the target does not.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/build-a-resilient-scraper",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3giv/building_a_resilient_scraper_retries_backoff_and/",
"url_overridden_by_dest": "https://capzy.ai/blog/build-a-resilient-scraper",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:54:44+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/bL6881QYi7w3jTFXr64UKU2K_NqCeuWY0RJUBkzyy6U.jpeg?width=140&height=73&auto=webp&s=8ee2dd07dec0ffb984a278a58dc3a8f4874d9111",
"preview_image": "https://external-preview.redd.it/bL6881QYi7w3jTFXr64UKU2K_NqCeuWY0RJUBkzyy6U.jpeg?auto=webp&s=dc2f883f24e234c2081aca07c7f6e952cb34243a",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
},
{
"id": "1vh3g8d",
"fullname": "t3_1vh3g8d",
"scraped_at": "2026-08-18T09:36:55.069503+00:00",
"title": "CAPTCHA Farms vs AI Solving: Speed, Cost, and Reliability",
"author": "Capzy-ai",
"subreddit": "Capzy",
"selftext": "When you buy CAPTCHA solving, you are choosing between two very different paradigms whether you realize it or not. One routes each challenge to a human who types the answer. The other runs computer vision, machine learning, and behavioral modeling to solve it programmatically. They behave differently on latency, on cost at scale, and, most importantly, on the invisible score-based widgets that now make up a large share of real-world challenges. The short answer: human-powered solving is flexible and handles novel visual puzzles a model has never seen, but it is slower and cannot help at all with invisible captchas that never show a picture. Automated solving is faster and consistent at volume and is the only approach that can address behavioral and score-based widgets, but it depends on the quality of the models and the browser sessions behind it. Understanding the tradeoffs tells you which failures to expect and where each one hits a wall. # How each paradigm works Human-powered solving is what it sounds like. When a challenge appears, an image or a puzzle is forwarded to a pool of people who look at it and submit an answer, which is relayed back to your client as a token or a set of coordinates. The strength is generality: a person can read a distorted word or pick the traffic lights even on a design nobody has automated yet. Automated solving replaces the person with software. For image challenges that means computer vision and trained classifiers or detectors. For behavioral and invisible challenges it means generating the interaction signals a real user would produce and minting a token from a genuine, trusted browser session. There is no human in the loop, so throughput is bounded by compute rather than by how many people are awake. # Latency Latency is where the gap is most visible. A human has to receive the image, look at it, decide, and click, and that path includes queue time when demand spikes. Realistic per-challenge times for human solving run from several seconds into the tens of seconds under load, and they get worse exactly when you need them most, during a traffic surge. Automated solving is bounded by model inference and, for token minting, by how long a real browser session takes to produce a valid interaction. That is often faster and, more importantly, far more predictable. A model does not take a coffee break, and a solve at 3 a.m. costs the same wall-clock time as one at noon. # Cost at volume At low volume the two can look comparable. The divergence shows up as you scale. Human solving has a labor cost floor: every single solve consumes a person's time, so unit cost does not fall much no matter how many you buy. It scales linearly with people, and people are the expensive part. Automated solving front-loads its cost into building and running models, then amortizes that across every request. As volume climbs, the per-solve cost trends down because the expensive part, the model, is already built. >Human solving scales with headcount. Automated solving scales with compute. At high volume that difference in cost curve is the whole argument. The catch is quality. Cheap automated solving that guesses badly wastes money on failed submissions and retries, so raw unit price is not the number to optimize. End-to-end success rate on your actual targets is. # Consistency and the invisible-widget problem Consistency favors automation. A trained model applies the same logic to every challenge, so its behavior is repeatable and you can reason about its failure modes. A distributed pool of people has natural variance, different accuracy, different speed, different interpretations of an ambiguous image. But the decisive difference is invisible captchas. Modern widgets increasingly show no puzzle at all. They score the visitor silently on behavior, browser signals, and reputation, then either pass them or escalate. A human solver is useless here, because there is nothing to look at and no answer to type. You cannot forward a score to a person. Invisible and behavioral challenges can only be addressed by generating the right signals: a coherent fingerprint, natural interaction, a clean IP, and a token minted from a real browser session that the widget already trusts. That is fundamentally an automated, engine-driven problem, and it is where the human paradigm simply has no move. # Where Capzy sits Our approach is model-based computer vision for the visual challenges, combined with token minting from real, non-headless browser sessions for the behavioral and invisible ones. The visual side gives consistent, fast classification. The session side is what handles the score-based widgets that a human pool cannot touch. You submit a task and poll for the result: curl -X POST https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_KEY", "task": { "type": "AntiTurnstileTaskProxyLess", "websiteURL": "https://example.com/login", "websiteKey": "0xAAA..." } }' # then GET the token via /getTaskResult with the returned taskId You can see the supported task types on [the solvers page](https://capzy.ai/solvers). That session quality is not solving in isolation. It leans on the same primitives detection cares about everywhere: a real engine via the [Cloud Browser](https://capzy.ai/browser) or [Capium](https://capzy.ai/capium), a coherent profile from the [Fingerprint API](https://capzy.ai/fingerprints), and a clean IP from the [Proxies API](https://capzy.ai/proxies). A token minted from a clean session passes because the session earned it. # Closing Neither paradigm is universally better. Human solving is a general fallback for novel visual puzzles. Automated solving wins on latency, on cost at scale, and is the only thing that works against the invisible score-based widgets that increasingly dominate. Match the approach to what you are actually being blocked by, and remember that on modern widgets, quality of the underlying browser session, not who solves the picture, is what decides the outcome. Want to test automated solving on your real targets? [Create a free account](https://capzy.ai/auth/register), submit a task, and measure success rate on the sites you actually hit.",
"selftext_html": "<!-- SC_OFF --><div class="md"><p>When you buy CAPTCHA solving, you are choosing between two very different paradigms whether you realize it or not. One routes each challenge to a human who types the answer. The other runs computer vision, machine learning, and behavioral modeling to solve it programmatically. They behave differently on latency, on cost at scale, and, most importantly, on the invisible score-based widgets that now make up a large share of real-world challenges.</p> <p>The short answer: human-powered solving is flexible and handles novel visual puzzles a model has never seen, but it is slower and cannot help at all with invisible captchas that never show a picture. Automated solving is faster and consistent at volume and is the only approach that can address behavioral and score-based widgets, but it depends on the quality of the models and the browser sessions behind it. Understanding the tradeoffs tells you which failures to expect and where each one hits a wall.</p> <h1>How each paradigm works</h1> <p>Human-powered solving is what it sounds like. When a challenge appears, an image or a puzzle is forwarded to a pool of people who look at it and submit an answer, which is relayed back to your client as a token or a set of coordinates. The strength is generality: a person can read a distorted word or pick the traffic lights even on a design nobody has automated yet.</p> <p>Automated solving replaces the person with software. For image challenges that means computer vision and trained classifiers or detectors. For behavioral and invisible challenges it means generating the interaction signals a real user would produce and minting a token from a genuine, trusted browser session. There is no human in the loop, so throughput is bounded by compute rather than by how many people are awake.</p> <h1>Latency</h1> <p>Latency is where the gap is most visible. A human has to receive the image, look at it, decide, and click, and that path includes queue time when demand spikes. Realistic per-challenge times for human solving run from several seconds into the tens of seconds under load, and they get worse exactly when you need them most, during a traffic surge.</p> <p>Automated solving is bounded by model inference and, for token minting, by how long a real browser session takes to produce a valid interaction. That is often faster and, more importantly, far more predictable. A model does not take a coffee break, and a solve at 3 a.m. costs the same wall-clock time as one at noon.</p> <h1>Cost at volume</h1> <p>At low volume the two can look comparable. The divergence shows up as you scale. Human solving has a labor cost floor: every single solve consumes a person's time, so unit cost does not fall much no matter how many you buy. It scales linearly with people, and people are the expensive part.</p> <p>Automated solving front-loads its cost into building and running models, then amortizes that across every request. As volume climbs, the per-solve cost trends down because the expensive part, the model, is already built.</p> <blockquote> <p>Human solving scales with headcount. Automated solving scales with compute. At high volume that difference in cost curve is the whole argument.</p> </blockquote> <p>The catch is quality. Cheap automated solving that guesses badly wastes money on failed submissions and retries, so raw unit price is not the number to optimize. End-to-end success rate on your actual targets is.</p> <h1>Consistency and the invisible-widget problem</h1> <p>Consistency favors automation. A trained model applies the same logic to every challenge, so its behavior is repeatable and you can reason about its failure modes. A distributed pool of people has natural variance, different accuracy, different speed, different interpretations of an ambiguous image.</p> <p>But the decisive difference is invisible captchas. Modern widgets increasingly show no puzzle at all. They score the visitor silently on behavior, browser signals, and reputation, then either pass them or escalate. A human solver is useless here, because there is nothing to look at and no answer to type. You cannot forward a score to a person.</p> <p>Invisible and behavioral challenges can only be addressed by generating the right signals: a coherent fingerprint, natural interaction, a clean IP, and a token minted from a real browser session that the widget already trusts. That is fundamentally an automated, engine-driven problem, and it is where the human paradigm simply has no move.</p> <h1>Where Capzy sits</h1> <p>Our approach is model-based computer vision for the visual challenges, combined with token minting from real, non-headless browser sessions for the behavioral and invisible ones. The visual side gives consistent, fast classification. The session side is what handles the score-based widgets that a human pool cannot touch. You submit a task and poll for the result:</p> <pre><code>curl -X POST https://api.capzy.ai/createTask \ -H "Content-Type: application/json" \ -d '{ "clientKey": "YOUR_KEY", "task": { "type": "AntiTurnstileTaskProxyLess", "websiteURL": "https://example.com/login", "websiteKey": "0xAAA..." } }' # then GET the token via /getTaskResult with the returned taskId </code></pre> <p>You can see the supported task types on <a href="https://capzy.ai/solvers">the solvers page</a>.</p> <p>That session quality is not solving in isolation. It leans on the same primitives detection cares about everywhere: a real engine via the <a href="https://capzy.ai/browser">Cloud Browser</a> or <a href="https://capzy.ai/capium">Capium</a>, a coherent profile from the <a href="https://capzy.ai/fingerprints">Fingerprint API</a>, and a clean IP from the <a href="https://capzy.ai/proxies">Proxies API</a>. A token minted from a clean session passes because the session earned it.</p> <h1>Closing</h1> <p>Neither paradigm is universally better. Human solving is a general fallback for novel visual puzzles. Automated solving wins on latency, on cost at scale, and is the only thing that works against the invisible score-based widgets that increasingly dominate. Match the approach to what you are actually being blocked by, and remember that on modern widgets, quality of the underlying browser session, not who solves the picture, is what decides the outcome.</p> <p>Want to test automated solving on your real targets? <a href="https://capzy.ai/auth/register">Create a free account</a>, submit a task, and measure success rate on the sites you actually hit.</p> </div><!-- SC_ON -->",
"url": "https://capzy.ai/blog/captcha-farms-vs-ai-solving",
"permalink": "https://www.reddit.com/r/Capzy/comments/1vh3g8d/captcha_farms_vs_ai_solving_speed_cost_and/",
"url_overridden_by_dest": "https://capzy.ai/blog/captcha-farms-vs-ai-solving",
"score": 1,
"upvote_ratio": 1,
"ups": 1,
"downs": 0,
"num_comments": 0,
"created_at": "2026-08-06T12:54:24+00:00",
"over_18": false,
"is_video": false,
"is_self": false,
"post_hint": "link",
"spoiler": false,
"locked": false,
"archived": false,
"pinned": false,
"is_original_content": false,
"distinguished": null,
"gilded": 0,
"edited": null,
"domain": "capzy.ai",
"flair": null,
"author_flair": null,
"author_premium": false,
"subreddit_subscribers": 5,
"subreddit_id": "t5_jbfcch",
"thumbnail": "https://external-preview.redd.it/SHSVHrr8Ul-Lv_QQuPgYO-7nm4JO_uSCr2-1LuAOrDs.jpeg?width=140&height=73&auto=webp&s=adcaefde62df0f5f2731f5a173306ea8c3790de1",
"preview_image": "https://external-preview.redd.it/SHSVHrr8Ul-Lv_QQuPgYO-7nm4JO_uSCr2-1LuAOrDs.jpeg?auto=webp&s=eec9e0d037c785d5a91ef94250a2fe1f7dbc1d59",
"preview_width": 1200,
"preview_height": 630,
"video": null,
"gallery": null,
"crosspost_parent": null,
"awards": 0,
"stickied": false,
"num_crossposts": 0
}
]
}
GET/v1/data/reddit/subreddits/{subreddit}/posts1 credit / run Verified

Subreddit Posts

Subreddit posts (hot/new/top/rising).

Handled for you:CloudflareRate Limit
Parameters
NameTypeReqDescription
subredditstringyesSubreddit name.
Advanced filters
sortstringnohot | new | top | rising | controversial (default: hot)
tstringnotop/controversial timeframe: hour|day|week|month|year|all (default: day)
limitintegerno1-100. (default: 25)
afterstringnoPagination cursor (from nextCursor).