Captcha OCR

API guide

Using the API

A JSON-over-HTTPS API at https://captchaocr.allvoraz.com. This page covers everything you need. The API reference has the same material as an OpenAPI spec with a try-it console.

Endpoints

MethodPathDoes
POST/v1/solveSolve one captcha
POST/v1/solve_batchSolve up to 256 captchas in parallel
GET/v1/balanceCredits left
POST/v1/reportReport a wrong answer and get the credit back
GET/healthService status (no key needed)
POST/createTask …Task API: createTask, getTaskResult, getBalance, reportIncorrect

Authentication

Every /v1 call needs an API key from Dashboard → API keys. Send it as Authorization: Bearer <key> (preferred) or X-API-Key: <key>. Keys start with co_live_. Only a hash is stored, so a lost key can't be recovered: revoke it and create a new one. Revoked keys stop working within 30 seconds. Keep keys on your servers. Never ship them in a browser or mobile app.

Solve a captcha: POST /v1/solve

curl -s https://captchaocr.allvoraz.com/v1/solve \
  -H "Authorization: Bearer $CAPTCHA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"image\": \"$(base64 < captcha.png | tr -d '\n')\", \"numeric\": true, \"length\": 6}"

Request body

FieldTypeMeaning
imagestring, requiredBase64 image: PNG, JPEG, GIF, WebP, TIFF, BMP or SVG, detected by content. A data:image/…;base64, prefix is fine. Up to 5 MB.
numericbooleanThe captcha may contain digits 0-9.
uppercasebooleanMay contain A-Z. Alone, lowercase readings are folded to uppercase.
lowercasebooleanMay contain a-z. Alone, uppercase readings are folded to lowercase.
lengthinteger 1-32Exact number of characters. The decoder returns the most likely reading of that length. Ignored with math.
mathbooleanArithmetic captcha (“14 - 10 = ?”); text is the computed answer.
hindibooleanOperands are Hindi number words (“इकतीस + 6 = ?”). Requires math.

With none of numeric, uppercase or lowercase set, digits and both cases are allowed and case is kept as read. Unknown fields are rejected (422), so typos don't pass silently.

Response

FieldTypeMeaning
textstringWhat to type. For math captchas, the computed answer.
reliablebooleanSubmit only when true. Only reliable answers are charged.
confidencenumber 0-1Probability of the least certain character.
engineocr | math | hindi_mathWhich engine read the image.
rawstringThe model's reading before post-processing (e.g. इकतीस+6).
expressionstring | nullMath only: what was computed, e.g. 31+6.
errorstring | nullinvalid_image (undecodable, not charged), model_unavailable, internal_error.
idintegerTask id, for /v1/report (kept 48 hours).
chargedbooleanWhether this answer cost a credit.

The X-Credits-Remaining response header carries your balance after the call.

Choosing options

Describe the captcha, not the answer you want. Each option rules out readings the model could otherwise make, which raises accuracy:

Captcha looks likeSend
482915: always 6 digits{"numeric": true, "length": 6}
14 + 9 = ? (answer: 23){"math": true}
PUKLX8: capitals and digits{"uppercase": true, "numeric": true}
hyi5ag: the site ignores case{"lowercase": true, "numeric": true}
7rX2Yx: mixed case, case mattersnothing (case is kept as read)
बयालीस - 6 = ? (answer: 36){"math": true, "hindi": true}
  • numeric alone also turns look-alike letters into digits (O→0, l→1, S→5, B→8).
  • uppercase or lowercase alone folds the other case in, and turns look-alike digits into letters.
  • length is enforced by the decoder itself. Set it whenever a site's captchas have a fixed length.

Acting on the result

if res.error:            # undecodable image: re-download it (not charged)
    ...
elif res.reliable:        # submit res.text (charged: 1 credit)
    ...
else:                     # unsure: refresh the captcha and solve the new one (not charged)
    ...

reliable combines the confidence with engine-specific checks: the length matched, a math answer is non-negative, a Hindi number word was read exactly. Refreshing a doubtful captcha is cheaper than a failed form submission, which can cost a page reload or a lockout.

Batches: POST /v1/solve_batch

Up to 256 images, solved in parallel, returned in request order. Top-level options apply to every image, and any item can override them. A plain base64 string works as shorthand for {"image": "…"}. A bad image fails only its own slot.

b64() { base64 < "$1" | tr -d '\n'; }
curl -s https://captchaocr.allvoraz.com/v1/solve_batch \
  -H "Authorization: Bearer $CAPTCHA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"numeric\": true, \"images\": [
        {\"image\": \"$(b64 a.png)\"},
        {\"image\": \"$(b64 b.png)\", \"length\": 5},
        {\"image\": \"$(b64 sum.png)\", \"math\": true}
      ]}"

Credits for the whole batch are reserved before solving (402 if the balance can't cover it), and the unreliable ones are returned before the response is sent.

Submit now, fetch later: POST /v1/submit + GET /v1/result/{id}

/v1/solve answers in the same request. When you'd rather not hold the connection (queues, workers, clients with short timeouts), submit the captcha instead. POST /v1/submit takes the same body and options, reserves a credit, and returns {"id": 184467, "status": "processing"} (HTTP 202) immediately. The captcha is solved in the background, usually within 0.1 s.

# Returns at once with a task id; the captcha is solved in the background.
curl -s https://captchaocr.allvoraz.com/v1/submit -H "Authorization: Bearer $CAPTCHA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"image\": \"$(base64 < captcha.png | tr -d '\n')\", \"numeric\": true, \"length\": 6}"
# {"id": 184467, "status": "processing"}

# Fetch the answer; wait=10 holds the request until it's ready (up to 10 s).
curl -s "https://captchaocr.allvoraz.com/v1/result/184467?wait=10" -H "Authorization: Bearer $CAPTCHA_API_KEY"
# {"id": 184467, "status": "ready", "result": {"text": "482915", "reliable": true, ...}, "error": null}

GET /v1/result/{id} returns the task:

statusMeaning
processingNot finished. Ask again in about half a second, or pass ?wait=N.
readyresult holds the answer: the same object /v1/solve returns. Check result.reliable.
failedThe solver couldn't run; error says why. The credit was returned. Submit again.

Add ?wait=10 (0-30 seconds) and the request returns the moment the answer is ready, so one call usually does it with no polling loop. Without it, poll:

# Without wait: "processing" until the answer is ready, then "ready" (or "failed").
curl -s https://captchaocr.allvoraz.com/v1/result/184467 -H "Authorization: Bearer $CAPTCHA_API_KEY"
# {"id": 184467, "status": "processing", "result": null, "error": null}

Billing is identical to /v1/solve: unreliable and undecodable results are refunded when the task finishes, and the task id works with /v1/report. Results are kept 48 hours. Each key can have up to 256 unfinished tasks (429 beyond that); another key's task ids return 404.

Billing, balance and refunds

  • 1 credit = 1 reliable answer. Undecodable images and low-confidence reads are free.
  • Buy credit packs on the billing page with UPI (Google Pay, PhonePe, Paytm), cards, netbanking or wallets. Credits never expire. See pricing.
  • GET /v1/balance returns {"credits": N, "alert_threshold": T} (alert_threshold is null without an alert). Every solve response also carries the balance in X-Credits-Remaining:
curl -s https://captchaocr.allvoraz.com/v1/balance -H "Authorization: Bearer $CAPTCHA_API_KEY"
# {"credits": 4321}

Low-balance alert: PUT /v1/alert

Each account can have one alert. When a solve leaves your credits below its threshold, we POST your JSON payload to your HTTPS url with your headers, so you top up before captchas start failing with 402.

# One alert per account. When your credits drop below 500, we POST the payload to your URL.
curl -s -X PUT https://captchaocr.allvoraz.com/v1/alert -H "Authorization: Bearer $CAPTCHA_API_KEY" \
  -H 'Content-Type: application/json' -d '{
    "threshold": 500,
    "url": "https://hooks.example.org/captcha-credits",
    "payload": {"text": "Captcha credits low: {{credits}} left (alert at {{threshold}})"},
    "headers": {"Authorization": "Bearer my-hook-secret"}
  }'
# {"threshold": 500, "url": "...", "armed": true, "last_status": null, ...}

curl -s -X POST https://captchaocr.allvoraz.com/v1/alert/test -H "Authorization: Bearer $CAPTCHA_API_KEY"
# {"delivered": true, "status": 200, "error": null}

curl -s https://captchaocr.allvoraz.com/v1/alert -H "Authorization: Bearer $CAPTCHA_API_KEY"          # read it
curl -s -X DELETE https://captchaocr.allvoraz.com/v1/alert -H "Authorization: Bearer $CAPTCHA_API_KEY" # remove it
FieldMeaning
thresholdFire when credits drop below this (1 or more).
urlYour HTTPS endpoint; it must resolve to a public address. Redirects are not followed.
payloadOptional JSON object, up to 8 KB. {{credits}}, {{threshold}} and {{time}} are filled in; a value that is exactly "{{credits}}" becomes a number. Default: {"event": "low_balance", "credits": N, "threshold": T}.
headersOptional, up to 10, e.g. Authorization, so your endpoint can verify the call.
  • It fires once per drop below the threshold (armed becomes false) and re-arms when a purchase brings your balance back to the threshold or above.
  • Any 2xx answer counts as delivered; otherwise we retry after 10 s and 60 s. Requests carry X-CaptchaOCR-Event: low_balance.
  • GET /v1/alert shows the alert and its last delivery, DELETE /v1/alert removes it, and POST /v1/alert/test sends it now (without disarming it) and returns what your endpoint answered.

If a site rejects a reliable answer, send its id to POST /v1/report within 48 hours and the credit comes back. Refunds per day are capped at 10% of that day's charged solves, plus 5. The response says refunded, and if not, a reason: already_reported, not_charged, unknown_task or daily_refund_limit.

curl -s https://captchaocr.allvoraz.com/v1/report -H "Authorization: Bearer $CAPTCHA_API_KEY" \
  -H 'Content-Type: application/json' -d '{"id": 184467}'
# {"refunded": true, "reason": null, "credits": 4322}

Errors

Errors are JSON: {"detail": "…"}, plus credits on 402. Every SDK raises an API exception carrying the status and body. A bad image is not an error: it's a normal 200 result with error set.

StatusMeaningWhat to do
200 + error: invalid_imageThe image can't be decoded.Not charged. Re-download the captcha.
401 invalid_api_keyMissing, wrong or revoked key.Check Authorization / X-API-Key.
402 insufficient_creditsBalance can't cover the request; body has credits.Buy credits on the dashboard.
422Invalid options: unknown field, hindi without math, length out of 1-32, 0 or >256 images.Fix the request; don't retry.
404 unknown_task / no_alertGET /v1/result: no such task for this key, or older than 48 hours. /v1/alert: none set.Check the id; submit again. Set an alert first.
429 too_many_concurrent_requestsMore than 32 requests in flight, or 256 unfinished submitted tasks, on one key.Retry after the Retry-After header.
503 solver_unavailableTemporary problem; credits were returned.Retry with exponential backoff.

Limits and performance

  • About 25 ms per text captcha and 6 ms per math captcha, plus network time.
  • 32 concurrent requests and 256 unfinished submitted tasks per key (429 beyond that), 256 images per batch, 5 MB per image.
  • Reuse one HTTP client or SDK client: keep-alive saves a TLS handshake on every call.
  • Results are kept 48 hours for /v1/result and reports, then deleted. Images are not stored.

Accuracy

Measured on real captchas from Indian government portals. None of these images were used for training, unless noted.

CaptchaSampleAnswers correct
Hindi-number arithmetic (state land-records portal)175 live captchas, never trained on100%
Arithmetic (state land-records portal)120 live captchas95.0%; of answers marked reliable, 98.3%
Text captchas, 46 government sites465 captchas, compared with human-verified answers88.0% agreement (88.6% with length/numeric set)

For text captchas, 87.5% of answers are marked reliable, and 92.6% of those match the reference answer. Several mismatches are mistakes in the reference itself, so real precision is higher. Accuracy varies by site: test on your captchas with the free credits before you scale up.

Versioning

The API and every SDK share one semantic version (now 1.0.0). Adding fields is a minor release, and clients must ignore unknown response fields. Removing or renaming a field would be a new major version under a new path.