CAPTCHA API / API reference
Bounding Box CAPTCHA Send one image and get object boundaries as xMin, xMax, yMin, and yMax.
Use this for challenges that ask the user to draw a box around an object or region.
Input Parameters Response Code examples 01 / Prepare
Prepare the input Send the original image and name the object or region that should be outlined. Ask for a tight boundary if you need a precise crop. Enable canNoAnswer only when the requested object may genuinely be absent.
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
Boxes are measured from the top-left corner of the uploaded image. Ask for tight boxes when the downstream workflow needs precise cropping. Use canNoAnswer when the object may not be present in every challenge. 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 bounding-box. All fields below belong to the JSON body.
Required headers Authentication guide kindstring required Always bounding-box. imagestring required JPEG, PNG, or GIF as Base64 or a data URL. Maximum 600 KB; keep both dimensions at or below 1000 px. canNoAnswerboolean optional Allow a no-match answer if the object is absent. Default: false. 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" : "bounding-box" ,
"image" : "data:image/png;base64,iVBORw0KGgoAAA..." ,
"comment" : "Draw a tight box around the green apple." ,
"canNoAnswer" : false
} 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.
Each box has left (xMin), right (xMax), top (yMin) and bottom (yMax) edges measured in original-image pixels. The values are edges, not width and height. Derive dimensions by subtracting the minimum from the maximum.
solution.bounding_boxes{xMin, xMax, yMin, yMax}[] Rectangle edges in original-image pixels, measured from the top-left. Width = xMax - xMin; height = yMax - yMin. solutionobject With canNoAnswer enabled, handle an empty or no-match answer before accessing rectangle values. 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" : "bounding-box" ,
"status" : "ready" ,
"solution" : {
"bounding_boxes" : [
{
"xMin" : 310 ,
"xMax" : 385 ,
"yMin" : 231 ,
"yMax" : 308
}
]
},
"solveCount" : 1
}
} 201 Created / selected fields {
"task" : {
"id" : "11111111-1111-4111-8111-111111111111" ,
"kind" : "bounding-box" ,
"status" : "processing" ,
"solution" : null
}
} Task-level failure / HTTP 201 or 200 {
"task" : {
"id" : "11111111-1111-4111-8111-111111111111" ,
"kind" : "bounding-box" ,
"status" : "failed" ,
"solution" : null ,
"errorCode" : "ERROR_CAPTCHA_UNSOLVABLE" ,
"errorDescription" : "Captcha task could not be solved."
}
} Read the solution / JavaScript const boxes = task.solution.bounding_boxes;
if (Array. isArray (boxes)) {
const rectangles = boxes. map ( box => ({
x: box.xMin, y: box.yMin,
width: box.xMax - box.xMin,
height: box.yMax - box.yMin,
}));
console. log (rectangles);
} 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" : "bounding-box" ,
"comment" : "Draw a tight box around the green apple." ,
"canNoAnswer" : false
};
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": "bounding-box",
"comment": "Draw a tight box around the green apple . ",
"canNoAnswer": false
} ''' )
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": "bounding-box",
"image": "data:image/png;base64,iVBORw0KGgoAAA...",
"comment": "Draw a tight box around the green apple.",
"canNoAnswer": false
}' 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.