AWS Lambda permet de traiter la résolution CAPTCHA sans maintenir de serveur permanent. C'est un bon choix si vous avez des besoins intermittents, des pics de charge ou un pipeline déjà construit autour d'API Gateway, SQS ou Step Functions. Vous payez l'exécution, vous laissez AWS gérer le scaling, et vous gardez une architecture simple à déployer.
L'enjeu n'est pas seulement d'appeler CaptchaAI depuis une fonction. Il faut aussi gérer proprement la clé API, calibrer les timeouts et choisir un mode de déclenchement adapté à votre charge.
Handler Lambda
# lambda_function.py
import json
import os
import time
import urllib.request
import urllib.parse
def lambda_handler(event, context):
"""AWS Lambda handler for CaptchaAI solving."""
api_key = os.environ["CAPTCHAAI_KEY"]
# Parse input
body = json.loads(event.get("body", "{}")) if isinstance(event.get("body"), str) else event
method = body.get("method", "userrecaptcha")
params = body.get("params", {})
try:
token = solve_captcha(api_key, method, params)
return {
"statusCode": 200,
"body": json.dumps({"token": token}),
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps({"error": str(e)}),
}
def solve_captcha(api_key, method, params, timeout=90):
"""Solve CAPTCHA using CaptchaAI API."""
# Submit task
submit_data = urllib.parse.urlencode({
"key": api_key,
"method": method,
"json": 1,
**params,
}).encode()
req = urllib.request.Request(
"https://ocr.captchaai.com/in.php",
data=submit_data,
)
with urllib.request.urlopen(req, timeout=30) as resp:
result = json.loads(resp.read())
if result.get("status") != 1:
raise RuntimeError(f"Submit error: {result.get('request')}")
task_id = result["request"]
# Poll for result
start = time.time()
while time.time() - start < timeout:
time.sleep(5)
poll_url = (
f"https://ocr.captchaai.com/res.php"
f"?key={api_key}&action=get&id={task_id}&json=1"
)
with urllib.request.urlopen(poll_url, timeout=15) as resp:
data = json.loads(resp.read())
if data["request"] != "CAPCHA_NOT_READY":
if data.get("status") == 1:
return data["request"]
raise RuntimeError(f"Solve error: {data['request']}")
raise TimeoutError("Solve timeout")
Sécuriser la clé API avec Secrets Manager
import json
import boto3
def get_api_key():
"""Retrieve CaptchaAI key from AWS Secrets Manager."""
client = boto3.client("secretsmanager")
response = client.get_secret_value(SecretId="captchaai/api-key")
secret = json.loads(response["SecretString"])
return secret["api_key"]
Création du secret :
aws secretsmanager create-secret \
--name captchaai/api-key \
--secret-string '{"api_key":"YOUR_API_KEY"}'
Template SAM pour l'infrastructure
# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Timeout: 120
MemorySize: 256
Runtime: python3.11
Resources:
CaptchaSolverFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_function.lambda_handler
Environment:
Variables:
CAPTCHAAI_KEY: !Sub "{{resolve:secretsmanager:captchaai/api-key:SecretString:api_key}}"
Events:
SolveApi:
Type: Api
Properties:
Path: /solve
Method: post
Policies:
- AWSSecretsManagerGetSecretValuePolicy:
SecretArn: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:captchaai/api-key-*"
Outputs:
SolveApiUrl:
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/solve"
Déploiement
# Build and deploy
sam build
sam deploy --guided
# Test
curl -X POST https://YOUR_API_ID.execute-api.us-east-1.amazonaws.com/Prod/solve \
-H "Content-Type: application/json" \
-d '{
"method": "userrecaptcha",
"params": {
"googlekey": "SITE_KEY",
"pageurl": "https://example.com"
}
}'
Traitement batch déclenché par SQS
Si vous avez beaucoup de résolutions à exécuter, une file SQS en entrée permet de découpler la soumission des tâches de leur exécution.
import json
import os
import time
import urllib.request
import urllib.parse
def sqs_handler(event, context):
"""Process CAPTCHA tasks from SQS queue."""
api_key = os.environ["CAPTCHAAI_KEY"]
results = []
for record in event["Records"]:
task = json.loads(record["body"])
try:
token = solve_captcha(
api_key,
task["method"],
task["params"],
)
results.append({
"task_id": task.get("id"),
"status": "success",
"token": token[:50],
})
except Exception as e:
results.append({
"task_id": task.get("id"),
"status": "error",
"error": str(e),
})
return {"results": results}
Points d'exploitation Lambda
| Facteur | Valeur |
|---|---|
| Timeout maximum | 15 minutes, mais 120 s suffisent souvent |
| Mémoire | 256 Mo suffisent dans la plupart des cas |
| Concurrence | 1000 exécutions par défaut selon le compte |
| Cold start | Faible impact face au temps de résolution |
| Coût | Très bas par invocation, hors coût API |
| Dépendances | urllib évite d'ajouter des layers inutiles |
Depannage
| Probleme | Cause probable | Correctif |
|---|---|---|
| La fonction expire | Timeout Lambda trop court | Passez à 120 s ou plus |
| Accès refusé au secret | Politique IAM absente | Ajoutez la permission Secrets Manager |
| Latence au premier appel | Cold start visible | Activez la provisioned concurrency si nécessaire |
| Import requests indisponible | Dépendance non embarquée | Utilisez urllib.request ou ajoutez un layer |
FAQ
Lambda est-il pertinent économiquement ?
Oui, surtout si la charge est variable. Vous évitez un serveur dédié pour un composant qui n'a pas besoin de tourner en permanence.
Quel timeout faut-il configurer ?
Dans la plupart des cas, 120 secondes suffisent. Si vous traitez des types plus lourds ou si votre pipeline ajoute de la latence réseau, prévoyez une marge supplémentaire.
Faut-il embarquer requests dans Lambda ?
Pas obligatoirement. Pour cet usage, urllib.request suffit souvent et simplifie nettement le package de déploiement.
Guides connexes
- Fonctions Google Cloud + CaptchaAI
- Fonctions Azure + CaptchaAI
Passez à une résolution serverless : récupérez votre clé CaptchaAI et branchez-la sur votre pipeline AWS.