The migration
CLORO_API_KEY at your anymorph key. Your webhook URL, idempotency keys, task
types, and response parsing all stay as they are.
What is implemented
Supported
POST /v1/async/taskPOST /v1/async/task/batchGET /v1/async/task/{id}Webhook callbacksNot implemented
/v1/monitor/* synchronous endpoints/v1/async/status/v1/monitor/* family is deliberately absent. Every engine here is driven
asynchronously; a request that blocks for the minutes a browser run can take is not a shape
this service offers.
Where behaviour differs
1. Throughput varies sharply by engine
1. Throughput varies sharply by engine
The most important difference, and the only one that can bite you in production. Cloro
absorbs your submission rate behind its own infrastructure. Here, each engine’s capacity is
the capacity of its backing pool.
GEMINI sustains ~14 QPS. PERPLEXITY sustains ≤2. Browser-driven engines sustain much
less. Over-submitting does not produce a 429 — it produces a growing queue and, eventually,
FAILED tasks.See Rate limits and capacity.2. Credits are nominal, never billed
2. Credits are nominal, never billed
credits is present on every response because Cloro clients read it. Submit and poll
responses report { "creditsToCharge": 0, "creditsCharged": 0 }; webhooks report a
per-engine figure mirroring Cloro’s pricing table.Nothing is metered. If you aggregate credits for cost reporting, the numbers will not mean
what they used to.3. Two engines can reject a submission outright
3. Two engines can reject a submission outright
CHATGPT and CLAUDE are region-gated. If no account can serve payload.country, the
submission fails immediately with 422 REGION_UNAVAILABLE rather than being accepted.Cloro has no equivalent. A client that assumes every well-formed submission is accepted
needs a branch here.4. include flags are mostly inert
4. include flags are mostly inert
Cloro’s
payload.include flags are accepted, but most engines here return a fixed-shape
response and ignore them. Only ChatGPT’s anonymous mode honours the full set; several
engines honour markdown and rawResponse only.Requesting html from Gemini, for instance, is silently ignored — no error, no field.
See the flag support table.6. A failed task adds a top-level `error` string
6. A failed task adds a top-level `error` string
GET /v1/async/task/{id} on a FAILED task returns a top-level error field that Cloro’s
TaskStatusResponse does not define — and it is a plain string, not the
{code, message, timestamp} object used by request-level errors.body.error as a failed request will misread a
successfully-retrieved failed task. Branch on task.status instead.These differences were established by calling
api.cloro.dev directly on 2026-07-09, not
by reading its spec. Cloro’s published OpenAPI disagrees with its own API on the validation
status code (422 documented, 400 returned) and on the shape of the validation error
body (a string, versus the object the API actually sends). Where the two conflict, this
API follows the live response.Engine name mapping
Cloro’s synchronous paths map ontotaskType values like this:
GOOGLE requesting AI Overview is a convention inherited from the original client, not a
Cloro taskType in its own right. The response is a SERP envelope with aioverview as a
nullable member — see the GOOGLE shape.CLAUDE, DEEPSEEK, AMAZON, NAVER_AI_BRIEF,
NAVER_AI_MODE.
Verifying the cutover
Run both APIs in parallel for a window and diff the fields you actually consume — in practiceresponse.text, response.sources[], and response.markdown. A shadow
comparison catches shape drift that a schema check will not, such as a source list that is
populated but ordered differently.