CAPTCHA API / API reference
Normal CAPTCHA Send a CAPTCHA image and get back the text that should be typed.
Use this for distorted image text challenges where the expected answer is a string.
Input Parameters Response Code examples 01 / Prepare
Prepare the input Send the image that contains the distorted characters. This solution reads text from an image; use Text CAPTCHA for a written question instead. Set caseSensitive, numeric or length hints only when you know the challenge rules.
Each task reserves $0.015 from your wallet. Successful tasks cost 1.5 times the provider cost; unused reserved credit is returned. Failed tasks are not charged. Billing details
Use the smallest clear crop of the CAPTCHA text. Turn on caseSensitive only when the target site requires exact casing. Set numeric or length constraints when they are known; it helps reduce ambiguous answers. For the complete examples, prepare challenge.png . You can change these filenames in the code.
File encoding & limits → Task lifecycle Create a task Send one POST with your input. Wait for the answer Poll the saved ID every 5–10 seconds. Use the result Read solution when status is ready. Polling & safe retries → 02 / Request
Request parameters POST /api/v1/captcha/tasks
Send the JSON object directly, without a task wrapper. Set kind to normal. All fields below belong to the JSON body.
Required headers Authentication guide kindstring required Always normal. imagestring required Base64 string or data URL for one JPEG, PNG, or GIF. Maximum 100 KB and 1000 px on either side. phraseboolean optional Set true when the answer must contain at least two space-separated words. Default: false (no preference). caseSensitiveboolean optional Preserve uppercase and lowercase. Default: false. numericinteger optional 0: any characters; 1: digits only; 2: letters only; 3: digits OR letters; 4: both digits AND letters. Default: 0. mathboolean optional Return the calculated answer to a math expression. Default: false. minLengthinteger optional Minimum answer length, 0 to 100. Default: 0 (unspecified). maxLengthinteger optional Maximum answer length, 0 to 100. Default: 0 (unspecified). When set, use a value at least as large as minLength. Decoded file limits use 1 KB = 1,000 bytes. The complete JSON payload must fit within 5,000,000 bytes after Base64 encoding.
JSON body
{
"kind" : "normal" ,
"image" : "data:image/png;base64,iVBORw0KGgoAAA..." ,
"comment" : "Type the text exactly as shown." ,
"phrase" : false ,
"caseSensitive" : false ,
"numeric" : 0 ,
"math" : false ,
"minLength" : 0 ,
"maxLength" : 0
} Base64 values here are placeholders. The complete examples below encode your local files automatically.
Base URL https://www.scrapingbuddy.com/api/v1
03 / Result
Understand the result Creation returns HTTP 201. Fetching the task returns HTTP 200. Both wrap the task in task; always check its status before reading the solution.
Read solution.text and keep it as a string. Converting 0074 to a number would lose the leading zeros. Preserve letter case when the challenge is case-sensitive. With math enabled, the answer is the calculation result rather than a transcription.
solution.textstring Recognized answer. Preserve leading zeros and case. With math enabled, this is the calculated answer. Shared task fields idUUID Save this ID to fetch the task or contact support. statusstring submitting or processing: wait. ready: use solution. failed: stop and read the error. solutionobject | null Null until a result is available. The shape depends on the CAPTCHA type. coststring | null Provider-reported USD cost; not the customer debit. chargedecimal string Actual customer charge in USD: provider cost multiplied by 1.5. Zero before settlement or for legacy tasks. billingStatusstring reserved, charged, released, awaiting_credit, review, or legacy. A review requires support; awaiting_credit needs a top-up. solveCountnumber | null Provider-reported solving attempts when available. Ready Processing Failed
{
"task" : {
"id" : "11111111-1111-4111-8111-111111111111" ,
"kind" : "normal" ,
"status" : "ready" ,
"solution" : {
"text" : "W68HP"
},
"solveCount" : 1
}
} 201 Created / selected fields {
"task" : {
"id" : "11111111-1111-4111-8111-111111111111" ,
"kind" : "normal" ,
"status" : "processing" ,
"solution" : null
}
} Task-level failure / HTTP 201 or 200 {
"task" : {
"id" : "11111111-1111-4111-8111-111111111111" ,
"kind" : "normal" ,
"status" : "failed" ,
"solution" : null ,
"errorCode" : "ERROR_CAPTCHA_UNSOLVABLE" ,
"errorDescription" : "Captcha task could not be solved."
}
} Read the solution / JavaScript const answer = task.solution.text;
if ( typeof answer !== "string" ) throw new Error ( "Missing text answer" );
console. log (answer); // Preserve leading zeros and letter case. 04 / Integrate
Complete examples These Node.js and Python scripts prepare the input, create one task and poll until it is ready or failed. They stop waiting after approximately 3 minutes. A client timeout does not cancel the task.
Set SCRAPINGBUDDY_API_KEY in your server environment. Place the input files beside your script, or update the filenames. For Node.js, use version 20+ and save as solve.mjs. For Python, install requests and save as solve.py. Run the script and retain the printed request and task IDs. cURL shows task creation only. Use the GET example to retrieve its result.
Keep credentials on your server Examples use the public API and create real tasks. They are not a free sandbox. Do not run them in browser code or expose your key.
Node.js Python cURL
solve.mjs / node solve.mjs // Node.js 20+. Run: node solve.mjs
import { readFile } from "node:fs/promises" ;
import { randomUUID } from "node:crypto" ;
const base = "https://www.scrapingbuddy.com/api/v1" ;
if ( ! process.env. SCRAPINGBUDDY_API_KEY ) throw new Error ( "Set SCRAPINGBUDDY_API_KEY." );
const auth = { Authorization: "Bearer " + process.env. SCRAPINGBUDDY_API_KEY };
const encode = async ( path ) => ( await readFile (path)). toString ( "base64" );
const body = {
"kind" : "normal" ,
"comment" : "Type the text exactly as shown." ,
"phrase" : false ,
"caseSensitive" : false ,
"numeric" : 0 ,
"math" : false ,
"minLength" : 0 ,
"maxLength" : 0
};
body.image = await encode ( "challenge.png" );
async function api ( path , options = {}) {
const response = await fetch (base + path, {
... options,
headers: { ... auth, "Content-Type" : "application/json" , ... options.headers },
signal: AbortSignal. timeout ( 30000 ),
});
const data = await response. json (). catch (() => ({}));
if ( ! response.ok) throw new Error (response.status + ": " + (data.error || response.statusText)
+ "; Retry-After: " + response.headers. get ( "Retry-After" ));
return data;
}
// Retain this ID; never retry POST concurrently or automatically after a timeout.
const requestId = randomUUID ();
console. log ( "Request ID:" , requestId);
const created = await api ( "/captcha/tasks" , {
method: "POST" ,
headers: { "Idempotency-Key" : requestId },
body: JSON . stringify (body)
});
let task = created.task;
console. log ( "Task ID:" , task.id);
const deadline = Date. now () + 180_000 ;
while (task.status === "submitting" || task.status === "processing" ) {
if (Date. now () >= deadline) throw new Error ( "Still processing. Resume GET polling for " + task.id);
await new Promise (( resolve ) => setTimeout (resolve, 6000 ));
task = ( await api ( "/captcha/tasks/" + task.id, { headers: {} })).task;
}
if (task.status !== "ready" ) throw new Error (task.errorCode + ": " + task.errorDescription);
console. log (task.solution); solve.py / python solve.py # Python 3. Install: pip install requests. Run: python solve.py
import base64, json, os, time, uuid, requests
from pathlib import Path
BASE = "https://www.scrapingbuddy.com/api/v1"
auth = { "Authorization" : "Bearer " + os.environ[ "SCRAPINGBUDDY_API_KEY" ]}
def encode (path):
return base64.b64encode(Path(path).read_bytes()).decode( "ascii" )
body = json.loads( r ''' {
"kind": "normal",
"comment": "Type the text exactly as shown . ",
"phrase": false,
"caseSensitive": false,
"numeric": 0,
"math": false,
"minLength": 0,
"maxLength": 0
} ''' )
body[ "image" ] = encode( "challenge.png" )
# Retain this ID; do not automatically retry POST after a timeout.
request_id = str (uuid.uuid4())
print ( "Request ID:" , request_id)
response = requests.post(
BASE + "/captcha/tasks" ,
json = body,
headers = { ** auth, "Content-Type" : "application/json" , "Idempotency-Key" : request_id},
timeout = 30 ,
)
response.raise_for_status()
task = response.json()[ "task" ]
print ( "Task ID:" , task[ "id" ])
deadline = time.monotonic() + 180
while task[ "status" ] in ( "submitting" , "processing" ):
if time.monotonic() >= deadline:
raise TimeoutError ( "Still processing. Resume GET polling for " + task[ "id" ])
time.sleep( 6 )
poll = requests.get( BASE + "/captcha/tasks/" + task[ "id" ], headers = auth, timeout = 30 )
poll.raise_for_status()
task = poll.json()[ "task" ]
if task[ "status" ] != "ready" :
raise RuntimeError ( f " { task.get( 'errorCode' ) } : { task.get( 'errorDescription' ) } " )
print (task[ "solution" ]) Bash / create request only # Keep your secret in an environment variable.
export SCRAPINGBUDDY_API_KEY = "YOUR_API_KEY"
# Bash. Replace sample Base64 values before sending.
# Generate and retain one UUID v4 per new task; do not retry concurrently.
export TASK_IDEMPOTENCY_KEY = "$( uuidgen )"
curl -X POST "https://www.scrapingbuddy.com/api/v1/captcha/tasks" \
-H "Authorization: Bearer $SCRAPINGBUDDY_API_KEY " \
-H "Idempotency-Key: $TASK_IDEMPOTENCY_KEY " \
-H "Content-Type: application/json" \
-d '{
"kind": "normal",
"image": "data:image/png;base64,iVBORw0KGgoAAA...",
"comment": "Type the text exactly as shown.",
"phrase": false,
"caseSensitive": false,
"numeric": 0,
"math": false,
"minLength": 0,
"maxLength": 0
}' When something goes wrong Check whether you received an HTTP error or a task with status: failed. Fix validation errors before retrying. For HTTP 429, pause for Retry-After; all requests share the account limit of 60 per minute.
If creation times out, do not automatically submit another task. Keep your request ID and check the existing task or Usage first.