Before you submit — these must be correct
If any of these are wrong, your token may be accepted by our solver but rejected or scored poorly by the target site. We can’t detect these mismatches for you.
- Whirl only: send captchaType "whirl". The slide / 3d / icon subtypes are experimental and not supported yet — don't rely on them in production.
- Send BOTH images: outerImage = the larger outer ring, innerImage = the smaller centre disc. Use raw base64 (strip any "data:image/...;base64," prefix) for the proxyless task, or outerImageUrl / innerImageUrl to have us fetch them.
- You perform the drag. Move the slider so the inner disc rotates by `angle` degrees: distance_px = (trackWidth - handleWidth) x angle / 360. Pass sliderWidth and we pre-compute `distance` for you.
- Drag like a human. TikTok scores the mouse path — use one smooth, eased, slightly-jittered forward motion over ~0.6-1.2s (optionally a tiny overshoot then settle). A linear/instant move or any back-and-forth is the #1 reason a correct angle still fails.
- One challenge per attempt. If a solve is rejected, reload the captcha to get a fresh image pair instead of re-dragging the same one.
What is TikTok Captcha?
TikTok — and the rest of ByteDance (CapCut, Lark, Douyin) — protects logins, signups, uploads and other risky actions with the ByteDance "secsdk" captcha. On the web it almost always appears as the Whirl challenge: two stacked circular images, a larger outer ring and a smaller inner disc, under the instruction "Drag the slider to fit the puzzle." You rotate the inner disc with a slider until its picture lines up with the outer ring. Capzy currently supports the Whirl / rotate subtype only. ByteDance also ships slide-puzzle, 3D-shape-match and icon-click variants, but on the web those are vanishingly rare (they live almost entirely in the mobile app). We have wired handlers for them, but they are experimental and not enabled — for production, stick to Whirl. This is a pure image-CV solve. You extract the two circle images from the page, send them to us, and we return the exact rotation angle. You apply the drag in your own browser/session. No proxy is required and the answer is device-independent, so it works no matter how the captcha is sized on screen.
How it works
Send Task
POST your TikTokCaptchaTaskProxyLess with the target URL and sitekey to our API. We'll queue it instantly.
We Solve
We unwrap both circles to polar coordinates and cross-correlate the outer ring against the inner disc along their shared seam, then refine the peak to sub-degree precision. The output is the exact angle the inner disc must turn to align — returned as angle (degrees), slideProportion (angle / 360) and a ready-to-use distance in pixels.
Get Token
Poll getTaskResult — when status is 'ready', the solution contains the token to inject into the target page.
Quick integration
import requests, time
API = "https://api.capzy.ai"
KEY = "capzy_your_key_here"
# Step 1: Create task
task = requests.post(f"{API}/createTask", json={
"clientKey": KEY,
"task": {
"type": "TikTokCaptchaTaskProxyLess",
"innerImage": "iVBORw0KGgoAAAANSUhEUg... (base64 of the inner disc)",
"outerImage": "/9j/4AAQSkZJRgABAQAAAQ... (base64 of the outer ring)",
"captchaType": "whirl",
"sliderWidth": "260"
}
}).json()
task_id = task["taskId"]
print(f"Task created: {task_id}")
# Step 2: Poll for result
while True:
result = requests.post(f"{API}/getTaskResult", json={
"clientKey": KEY,
"taskId": task_id
}).json()
if result["status"] == "ready":
print("Solved!", result["solution"])
break
elif result["status"] == "failed":
print("Failed:", result.get("errorDescription"))
break
time.sleep(1)
# Step 3: Rotate — apply the angle to the captcha image control
angle = result["solution"]["angle"]
# e.g. slider.set_rotation(angle) or POST angle=<value> to the site
print(f"rotate by {angle} degrees")Using your own proxy
Use TikTokCaptchaTask instead of TikTokCaptchaTaskProxyLess to route the solve through your own proxy. This is useful when the target site checks the IP that solved the captcha matches the IP submitting the form.
import requests, time
API = "https://api.capzy.ai"
KEY = "capzy_your_key_here"
# Step 1: Create task
task = requests.post(f"{API}/createTask", json={
"clientKey": KEY,
"task": {
"type": "TikTokCaptchaTask",
"proxyPort": "8080",
"proxyType": "http",
"proxyLogin": "user",
"captchaType": "whirl",
"sliderWidth": "260",
"proxyAddress": "1.2.3.4",
"innerImageUrl": "https://.../captcha/inner.png",
"outerImageUrl": "https://.../captcha/outer.jpg",
"proxyPassword": "pass"
}
}).json()
task_id = task["taskId"]
print(f"Task created: {task_id}")
# Step 2: Poll for result
while True:
result = requests.post(f"{API}/getTaskResult", json={
"clientKey": KEY,
"taskId": task_id
}).json()
if result["status"] == "ready":
print("Solved!", result["solution"])
break
elif result["status"] == "failed":
print("Failed:", result.get("errorDescription"))
break
time.sleep(1)
# Step 3: Rotate — apply the angle to the captcha image control
angle = result["solution"]["angle"]
# e.g. slider.set_rotation(angle) or POST angle=<value> to the site
print(f"rotate by {angle} degrees")additional proxy parameters
proxyTypetypestringreqnohttp / https / socks5. Only needed for the proxy variant, where you send image URLs instead of base64.proxyAddresstypestringreqyesProxy host/IP we use to download outerImageUrl / innerImageUrl.proxyPorttypenumberreqyesProxy port.proxyLogintypestringreqnoProxy username, if authenticated.proxyPasswordtypestringreqnoProxy password, if authenticated.Task parameters
captchaTypetypestringreqyesSet to "whirl" (aliases: rotate, spin). The only supported subtype today.outerImagetypestringreqyesBase64 of the larger OUTER ring image (strip any "data:image/...;base64," prefix). Or send outerImageUrl to have us fetch it.innerImagetypestringreqyesBase64 of the smaller INNER disc image (strip the data: prefix). Or send innerImageUrl.sliderWidthtypenumberreqnoRendered width (px) of the slider track in your page. When supplied we return `distance` in px; otherwise compute it yourself from slideProportion.Solution response
angletypenumberDegrees (0-360) to rotate the inner disc clockwise to align it. Drag distance = (trackWidth - handleWidth) x angle / 360.slideProportiontypenumberangle / 360 (0-1). Multiply by your rendered slider track width for a device-independent drag distance in px.distancetypenumberDrag distance in px, pre-computed from the sliderWidth you passed (0 if you didn't pass one — use slideProportion instead).Example response
Full getTaskResult response shape. The fields in the table above describe what's inside solution — the outer envelope (errorId, status) is identical for every captcha type.
{
"errorId": 0,
"status": "ready",
"solution": {
"angle": 287,
"distance": 207,
"slideProportion": 0.7972
}
}Error response
Failures use the same envelope with errorId: 1 plus errorCode + errorDescription. See the error-code reference for the full list.
{
"errorId": 1,
"errorCode": "ERROR_CAPTCHA_UNSOLVABLE",
"errorDescription": "Solver gave up — automatically refunded."
}Pending response
While the solver is still working, getTaskResult returns status: "processing". Poll every 1–2 seconds until ready or failed.
{
"errorId": 0,
"status": "processing"
}Features
task types
proxyless: TikTokCaptchaTaskProxyLess
with proxy: TikTokCaptchaTask