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.
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
-
PHP quickstart
Four lines plus one import to a solved captcha, and an honest statement of what adoption costs across a codebase.
-
Python quickstart
Four lines with httpx, five with requests. No monkey-patching, no runtime dependencies.
-
Troubleshooting
The three failures that account for most early integration problems, and the exact message each one produces.
-
HTTP API
createTask,getTaskResult,getBalance,reportCorrect,reportIncorrect.
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.
- You make the HTTP request you were going to make anyway, through the
SDK's drop-in (
Legibl\curl_execin PHP, an httpx transport or a requests adapter in Python). - The response body is inspected for a challenge. A body that cannot be read is reported, not guessed at.
- If the challenge is reachable by form injection, the SDK asks the Legibl
service for a solution over
createTask/getTaskResult. - The original request is resubmitted once, with the solution attached. It is never a general retry.
- Every request produces exactly one outcome, which your observer sees.
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.
| Outcome | Meaning |
|---|---|
no_challenge | Nothing to do. |
solved | Solution obtained and the request resubmitted. |
unhandled | A challenge was identified but is out of reach of form injection. |
unavailable | The SDK could not act, for reasons of its own rather than the target's. |
solve_failed | The service did not return a usable solution. |
expired | A solution was obtained but aged out before it could be submitted — our latency, not the solver's accuracy. |
passthrough | The 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.