ALTCHA
Solve the issued proof-of-work challenge. Use the signed challenge document issued by your ALTCHA integration. You can send its complete JSON object or its challenge endpoint URL.
{
"clientKey": "capzy_YOUR_API_KEY",
"task": {
"type": "AltchaTaskProxyLess",
"challenge": {
"algorithm": "SHA-256",
"challenge": "6abc3434b5ac6b2e509ee5aeae81b43b4ee80902850a3f3b52a2734478c9eee2",
"maxnumber": 50000,
"salt": "5206b8e45d88a37fc4e0247e",
"signature": "3debf2d432ec072ba6cc573581d24d77118f0230c2c3a3dc1e35725c5af7b879"
}
}
}Illustrative values. Replace the inputs with values from your current integration.
Capture the issued challenge.
Use the signed challenge document issued by your ALTCHA integration. You can send its complete JSON object or its challenge endpoint URL.
Your API key authenticates Capzy.
Provider keys, challenge IDs, images, and session values belong inside task.
Preserve the signed document unchanged.
- Inspect the widget's challenge configuration or the Network request that fetches its current challenge JSON.
- Copy the complete response object into task.challenge, preserving algorithm, challenge, salt, signature, maxnumber, and any additional original fields.
- Prefer this inline object when the challenge endpoint needs your browser/session context.
{
"challenge": {
"algorithm": "SHA-256",
"challenge": "YOUR_ISSUED_HASH",
"maxnumber": 50000,
"salt": "YOUR_ISSUED_SALT",
"signature": "YOUR_ISSUED_SIGNATURE"
}
}These illustrated values cannot form a valid signed challenge. Copy the actual document your application issued.
Provider integration documentationHow ALTCHA works
ALTCHA is an open-source, self-hostable proof-of-work captcha with no tracking and no puzzles. The server issues a challenge — {algorithm, challenge, salt, maxnumber, signature} — and the browser brute-forces the integer N whose hash matches. It's used by privacy-conscious sites that don't want to send data to Google or hCaptcha.
We run the same hash search the widget would — SHA-256 (default), plus SHA-384, SHA-512, and SHA-1 — in parallel across cores, and return the base64-encoded solution payload in the exact format the ALTCHA widget submits. Pass the challenge object, or a challengeURL we fetch (on AltchaTask, THROUGH your proxy with coherent browser headers, so managed ALTCHA/Sentinel endpoints that gate datacenter IPs still return a challenge), and get the token in milliseconds.
- Solves SHA-256 / SHA-384 / SHA-512 / SHA-1 proof-of-work in milliseconds
- Multi-core parallel search — trivial to tough challenges alike
- Output matches the official ALTCHA widget payload exactly
- Proxyless pure-compute, or fetch the challengeURL through your proxy (AltchaTask) to clear IP-gated endpoints
Know what goes in.
Choose a field to see its purpose, where to get it, and its place in the request. Optional values omitted from the example can be added when your flow uses them.
The request below follows your selected task configuration.
Request authentication
Required task inputs
Optional context
type
stringRequiredThe exact API identifier for the selected task variant and network mode.
Choose the task/network or request variant on this page. The integration examples update this exact API task identifier.
Open the input guideAltchaTaskProxyLess{
"clientKey": "capzy_YOUR_API_KEY",
"task": {
"type": "AltchaTaskProxyLess",
"challenge": {
"algorithm": "SHA-256",
"challenge": "6abc3434b5ac6b2e509ee5aeae81b43b4ee80902850a3f3b52a2734478c9eee2",
"maxnumber": 50000,
"salt": "5206b8e45d88a37fc4e0247e",
"signature": "3debf2d432ec072ba6cc573581d24d77118f0230c2c3a3dc1e35725c5af7b879"
}
}
}The selected field is highlighted when present. Preserve the declared value type and current challenge context.
Make your first request.
Create the task, keep its taskId, and poll until status is ready. Then read the selected variant's solution.
- Create the task
Send
clientKeyand the selectedtaskto/createTaskfrom your server. Keep the returnedtaskId. - Wait for the solution
Send that same key and ID to
/getTaskResult. While processing, wait two seconds before checking again. Stop on Ready or an error. - Apply the result
Use the encoded solution.token in the widget's configured form field, commonly altcha. Preserve the returned payload for integrations that consume its structured fields and validate against the issued challenge on your backend.
import json, os, time
import requests
API = "https://api.capzy.ai"
KEY = os.environ["CAPZY_API_KEY"]
task = json.loads("{\"type\":\"AltchaTaskProxyLess\",\"challenge\":{\"algorithm\":\"SHA-256\",\"challenge\":\"6abc3434b5ac6b2e509ee5aeae81b43b4ee80902850a3f3b52a2734478c9eee2\",\"maxnumber\":50000,\"salt\":\"5206b8e45d88a37fc4e0247e\",\"signature\":\"3debf2d432ec072ba6cc573581d24d77118f0230c2c3a3dc1e35725c5af7b879\"}}")
def call(path, payload):
response = requests.post(f"{API}/{path}", json={"clientKey": KEY, **payload}, timeout=30)
response.raise_for_status()
data = response.json()
if data.get("errorId", 0) or data.get("status") == "failed":
raise RuntimeError(data.get("errorDescription") or data.get("errorCode") or "Task failed")
return data
created = call("createTask", {"task": task})
for attempt in range(60):
result = call("getTaskResult", {"taskId": created["taskId"]})
if result.get("status") == "ready":
solution = result["solution"]
print(solution)
break
time.sleep(2)
else:
raise TimeoutError("Task is still processing; keep the taskId to check it later.")
Before runningReplace all illustrative inputs with your captured values. Set CAPZY_API_KEY in your environment. Install requests with pip install requests.
Polling and failuresThe example checks HTTP/API failures, uses 30-second request timeouts, and stops after 60 result checks. Retain taskId if your task is still processing.
Understand what comes back.
Follow this task from creation to completion. Response fields and usage follow your selected request variant.
ALTCHA token + payload
Use the encoded solution.token in the widget's configured form field, commonly altcha. Preserve the returned payload for integrations that consume its structured fields and validate against the issued challenge on your backend.
- string
- Base64-encoded solution payload — set it as the altcha-payload / altcha form field value (matches the official widget output), then submit
- object
- The decoded solution {algorithm, challenge, number, salt, signature, took} for inspection
{
"errorId": 0,
"status": "ready",
"solution": {
"token": "eyJhbGdvcml0aG0iOiJTSEEtMjU2IiwiY2hhbGxlbmdlIjoiMDAwMDAwZjJhNDU2Nzg5MCIsIm51bWJlciI6NjU0MzIxLCJzYWx0IjoidGVzdF9zYWx0Iiwic2lnbmF0dXJlIjoiIiwidG9vayI6MjV9",
"payload": {
"algorithm": "SHA-256",
"challenge": "000000f2a4567890",
"number": 654321,
"salt": "test_salt",
"signature": "",
"took": 25
}
}
}Illustrative values. Additional metadata may be returned; only Ready contains a completed solution.
Submit the encoded ALTCHA proof
The default widget field is altcha; match any custom name setting. Send solution.token, the encoded proof, rather than the decoded solution.payload object. Validate it on your backend against the challenge that issued it.
Execution context result is the parsed Ready response from the polling example. session, when referenced, is your existing requests.Session (or browser-compatible session) with the original cookie jar, headers, and solving proxy configured in session.proxies for both http and https. Keep this client for the complete flow. task is the task object you submitted. For Shape, action_body is the original protected request body as bytes or a string, with its original Content-Type in session.headers. Install curl_cffi when the fragment imports it; match the target's required browser/TLS identity.
token = result["solution"]["token"]
# Keep the original form's action, other fields, and session.
response = session.post("https://target.example.com/submit", data={
"altcha": token,
# Add the original application form fields here.
}, timeout=30)When something doesn't work.
Check your captured values, the selected variant, and the response before starting another task.
How do I use the token?
Set the returned token as the value of the ALTCHA form field (the hidden input the widget populates, usually named 'altcha' or 'altcha-payload'), then submit. Your server verifies it against the same salt/signature it issued.
Do I send the challenge or a URL?
Prefer the challenge object. If you already have the challenge JSON from your page, send the whole thing as 'challenge' — that's the reliable path. Only use challengeURL when that endpoint is publicly fetchable server-side; if it's behind a JS/WAF anti-bot we can't reach it. One of the two is required.
My task fails with 'challenge and salt are required' or ERROR_INVALID_PARAMS. Why?
You're passing the fields the wrong way. Send the FULL nested object: challenge: {algorithm, challenge, salt, maxnumber, signature}. Sending salt/signature/iterations as top-level task fields, or challenge:null, won't work — the 'challenge' hash is the target we solve for and it must be inside the challenge object. Note some widgets call maxnumber 'iterations'; pass its value as maxnumber.
The challengeURL times out or returns something odd.
Some deployments put the challenge endpoint behind a JS/WAF anti-bot, so a server-side GET never receives a real challenge (you'll see a stub or a block, not {algorithm, challenge, salt, ...}). Capture the challenge object in your browser — which already passed the gate — and pass it inline via 'challenge' instead of pointing us at the URL.
Connect ALTCHA to your workflow.
Get your Capzy API key and use this guide's integration.
AltchaTaskProxyLess · Live configuration