Legibl docs PHP 0.1.0-dev · Python 0.1.0

Legibl documentation

Legibl is a captcha-solving gateway with SDKs that put challenge handling inside the HTTP call you already make. The SDK never solves anything itself: it detects a challenge, asks the service for a solution, resubmits the blocked request once, and reports what happened.

What this documentation describes

PHP SDK 0.1.0-dev (unreleased — composer.json declares no version and the package is not on Packagist) and Python SDK 0.1.0. The gateway's public HTTP surface is specified but not yet deployed. See Versions for exactly what is and is not shipped.

Start here

What Legibl handles

Four captcha types, named exactly. Anything not in this table is not supported, and the SDK will tell you so rather than fail quietly.

Type Identifier Automatic in the SDKs Accepted by the API
reCAPTCHA v2 recaptcha_v2 yes yes
reCAPTCHA v2 invisible recaptcha_v2_invisible yes yes
Cloudflare Turnstile turnstile yes yes
Image OCR image_ocr detection only yes

hCaptcha, DataDome, Kasada and PerimeterX are not supported. There is no adapter, no task type and no detection for any of them. A page carrying one of those will be reported as no_challenge by the SDKs, because they genuinely do not find a challenge they know about — see Supported captcha types for the detail, including why image OCR reads “detection only” above.

How the SDKs work

Both SDKs share the same shape, and neither carries a browser engine or an inference runtime.

  1. You make the HTTP request you were going to make anyway, through the SDK's drop-in (Legibl\curl_exec in PHP, an httpx transport or a requests adapter in Python).
  2. The response body is inspected for a challenge. A body that cannot be read is reported, not guessed at.
  3. If the challenge is reachable by form injection, the SDK asks the Legibl service for a solution over createTask / getTaskResult.
  4. The original request is resubmitted once, with the solution attached. It is never a general retry.
  5. Every request produces exactly one outcome, which your observer sees.
Failure degrades handling, never availability

If the service is unreachable, slow, or returns an error, the solver returns nothing and your original response is passed through untouched. A scraper that hangs is worse than one that fails to solve.

The distinctions the SDKs refuse to collapse

Seven outcomes exist rather than a boolean, because the interesting failures are not the solver's fault and merging them would hide that.

OutcomeMeaning
no_challengeNothing to do.
solvedSolution obtained and the request resubmitted.
unhandledA challenge was identified but is out of reach of form injection.
unavailableThe SDK could not act, for reasons of its own rather than the target's.
solve_failedThe service did not return a usable solution.
expiredA solution was obtained but aged out before it could be submitted — our latency, not the solver's accuracy.
passthroughThe request failed or its body went somewhere unreadable; curl's own return value was handed back.

Each of these maps to a corrective action in Errors & outcomes.

This site

These docs are plain HTML and CSS with no build step and no package manager. They deploy as static files, independently of the gateway and the portal, so a documentation fix never carries the risk of a solver deploy — and they stay available when either application is not.