Retool lets teams build internal tools quickly. When those tools need to interact with CAPTCHA-protected external services — submitting forms, pulling data from portals, or automating third-party workflows — CaptchaAI handles the solving through Retool's REST API queries.
This guide shows how to set up CaptchaAI as a REST API resource in Retool, build queries to submit and retrieve CAPTCHA solutions, and wire them into your app's UI.
Real-World Scenario
Your ops team uses a Retool app to submit data to a government portal that requires reCAPTCHA v2. Instead of manually solving CAPTCHAs, the Retool app:
- Takes the sitekey and page URL as input
- Submits the CAPTCHA to CaptchaAI
- Polls for the result
- Displays the solved token for use in the form submission
Step 1: Create CaptchaAI as a REST API Resource
In Retool, navigate to Resources → Create New → REST API:
| Field | Value |
|---|---|
| Name | CaptchaAI |
| Base URL | https://ocr.captchaai.com |
| Authentication | None (API key sent as query parameter) |
Save the resource. You'll reference it in queries.
Step 2: Create the Submit Query
Create a new query called submitCaptcha:
| Setting | Value |
|---|---|
| Resource | CaptchaAI |
| Action Type | GET |
| URL Path | /in.php |
Query Parameters:
| Key | Value |
|---|---|
key |
{{secretsStore.CAPTCHAAI_API_KEY}} |
method |
userrecaptcha |
googlekey |
{{sitekeyInput.value}} |
pageurl |
{{pageurlInput.value}} |
json |
1 |
Store your API key in Retool's Secrets Store (Settings → Secrets) to avoid hardcoding it.
Transformer (optional):
// Parse the response
const data = {{ submitCaptcha.data }};
if (data.status === 1) {
return { taskId: data.request, status: 'submitted' };
}
return { error: data.request, status: 'failed' };
Step 3: Create the Poll Query
Create a query called pollResult:
| Setting | Value |
|---|---|
| Resource | CaptchaAI |
| Action Type | GET |
| URL Path | /res.php |
Query Parameters:
| Key | Value |
|---|---|
key |
{{secretsStore.CAPTCHAAI_API_KEY}} |
action |
get |
id |
{{submitCaptcha.data.request}} |
json |
1 |
Transformer:
const data = {{ pollResult.data }};
if (data.status === 1) {
return { token: data.request, status: 'solved' };
}
if (data.request === 'CAPCHA_NOT_READY') {
return { status: 'pending' };
}
return { error: data.request, status: 'error' };
Step 4: Build the Polling Loop with JavaScript
Create a JavaScript query called solveCaptcha that orchestrates the submit and poll cycle:
// solveCaptcha — JavaScript Query
async function solve() {
// Submit the CAPTCHA task
await submitCaptcha.trigger();
const submitResult = submitCaptcha.data;
if (submitResult.status !== 1) {
return { error: submitResult.request, status: 'submit_failed' };
}
const taskId = submitResult.request;
// Wait 15 seconds before first poll
await new Promise(r => setTimeout(r, 15000));
// Poll up to 20 times (100 seconds max)
for (let i = 0; i < 20; i++) {
await pollResult.trigger({
additionalScope: { taskId: taskId }
});
const result = pollResult.data;
if (result.status === 1) {
return { token: result.request, status: 'solved' };
}
if (result.request !== 'CAPCHA_NOT_READY') {
return { error: result.request, status: 'error' };
}
// Wait 5 seconds before next poll
await new Promise(r => setTimeout(r, 5000));
}
return { error: 'Polling timeout', status: 'timeout' };
}
return solve();
Step 5: Build the UI
Create a simple Retool app layout:
Input Section
- Text Input (
sitekeyInput): Label "reCAPTCHA Sitekey" - Text Input (
pageurlInput): Label "Page URL" - Button (
solveButton): Label "Solve CAPTCHA", onClick →solveCaptcha.trigger()
Status Section
- Text component:
{{ solveCaptcha.isFetching ? "Solving..." : "" }} - Loading Indicator: Visible when
{{ solveCaptcha.isFetching }}
Result Section
- Text Area (
tokenOutput): - Value:
{{ solveCaptcha.data?.token || '' }} - Label: "Solved Token"
- Read-only
- Copy Button: Copies token to clipboard
- Status Badge: Shows success/error based on
{{ solveCaptcha.data?.status }}
Step 6: Use the Token in Downstream Queries
Once solved, use the token in another Retool query that submits the form:
Create a query called submitForm:
| Setting | Value |
|---|---|
| Resource | Your target API |
| Action Type | POST |
| Body | Form data including g-recaptcha-response: {{solveCaptcha.data.token}} |
Wire this to a "Submit Form" button that's enabled only when {{ solveCaptcha.data?.status === 'solved' }}.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
ERROR_WRONG_USER_KEY |
API key not in Secrets Store or wrong value | Verify key in Settings → Secrets |
| Query returns raw text instead of JSON | Missing json=1 parameter |
Add json: 1 to query parameters |
| Polling timeout | CAPTCHA type needs longer solve time | Increase polling iterations from 20 to 30 |
submitCaptcha.data is undefined |
Query hasn't run yet | Ensure submit query runs before poll |
| JavaScript query timeout | Retool's 120-second limit for JS queries | Reduce polling to 20 iterations with 5-second intervals |
FAQ
Can I store the CaptchaAI API key securely in Retool?
Yes. Use Retool's Secrets Store (Environment → Secrets) and reference it as {{secretsStore.CAPTCHAAI_API_KEY}}. This keeps the key out of query definitions visible to app builders.
Does this work with Retool Cloud and Self-hosted?
Yes, both versions support REST API resources and JavaScript queries. Self-hosted Retool can also hit CaptchaAI's API directly without proxy requirements.
Can I build a reusable CAPTCHA-solving module in Retool?
Yes. Create a Retool Module with the submit/poll queries and UI components, then embed it in any app that needs CAPTCHA solving.
What CAPTCHA types can I solve through Retool?
Any type CaptchaAI supports — reCAPTCHA v2/v3, Cloudflare Turnstile, Image CAPTCHAs. Adjust the method and parameters in the submit query accordingly.
Related Articles
- How To Solve Recaptcha V2 Callback Using Api
- Recaptcha V2 Turnstile Same Site Handling
- Recaptcha V2 Callback Mechanism
Next Steps
Add CAPTCHA solving to your Retool internal tools — get your CaptchaAI API key and set up the REST API resource.
Related guides:
Discussions (0)
Join the conversation
Sign in to share your opinion.
Sign InNo comments yet.