API key yang di-hardcode dan timeout tetap 300 detik berjalan baik untuk skrip percobaan, tapi merepotkan begitu masuk production — setiap kali nilainya berubah, Anda harus edit kode, commit, lalu deploy ulang service. Panduan ini menyusun konfigurasi CaptchaAI yang production-ready: hierarki env variable → file config → default kode, config loader untuk Python dan Node.js, pemisahan staging vs production, dan secret management yang tidak pernah menaruh API key di version control.
Urutan Prioritas: Env Var, File Config, atau Default?
Config loader CaptchaAI membaca tiga lapis pengaturan dengan urutan prioritas tetap: env variable menang atas file config, file config menang atas default di kode:
Priority (highest → lowest):
1. Environment variables ← deployment-specific overrides
2. Config file (YAML/JSON) ← version-controlled defaults
3. Application defaults ← fallback values in code
Kalau service Anda punya lapisan CLI flag, taruh satu tingkat di atas env var — aturannya sama: pengaturan paling dekat dengan operator yang menang. Default di kode cukup untuk development lokal, file YAML menyimpan nilai per lingkungan yang di-commit ke repo, dan env variable jadi override deployment yang tidak pernah masuk Git.
Referensi Lengkap Variabel Konfigurasi
Sebelas variabel ini mengatur seluruh perilaku CaptchaAI di production — yang wajib cuma satu, sisanya sudah punya default yang aman:
| Parameter | Env Variable | Default | Deskripsi |
|---|---|---|---|
| API Key | CAPTCHAAI_API_KEY |
— | Wajib. API key CaptchaAI Anda |
| Submit URL | CAPTCHAAI_SUBMIT_URL |
https://ocr.captchaai.com/in.php |
Endpoint submit task |
| Poll URL | CAPTCHAAI_POLL_URL |
https://ocr.captchaai.com/res.php |
Endpoint polling hasil |
| Interval poll | CAPTCHAAI_POLL_INTERVAL |
5 |
Detik antar polling |
| Maks poll | CAPTCHAAI_MAX_POLLS |
60 |
Maksimum polling sebelum timeout |
| Concurrency | CAPTCHAAI_CONCURRENCY |
10 |
Maksimum task CAPTCHA paralel |
| Timeout | CAPTCHAAI_TIMEOUT |
300 |
Timeout keseluruhan dalam detik |
| Proxy | CAPTCHAAI_PROXY |
— | URL proxy untuk solve CAPTCHA |
| Callback URL | CAPTCHAAI_CALLBACK_URL |
— | URL webhook untuk hasil async |
| Retry | CAPTCHAAI_RETRIES |
3 |
Percobaan ulang untuk kegagalan sementara |
| Log level | CAPTCHAAI_LOG_LEVEL |
info |
Verbositas logging |
Menyamakan Concurrency dengan Thread pada Paket CaptchaAI Anda
CAPTCHAAI_CONCURRENCY menentukan berapa banyak task CAPTCHA yang aplikasi Anda kirim bersamaan — tapi kapasitas sebenarnya dibatasi oleh jumlah thread yang Anda beli di paket CaptchaAI, bukan oleh nilai di config Anda. CaptchaAI menagih per thread paralel dengan solve tak terbatas per thread: BASIC ($15/bulan, 5 thread), STANDARD ($30/bulan, 15 thread), ADVANCE ($90/bulan, 50 thread), sampai ENTERPRISE ($300/bulan, 200 thread) dan tingkat VIP untuk volume besar.
Ini relevan buat tim scraping dan price-monitoring di Indonesia yang mulai dari paket kecil lalu naik volume bertahap. Kalau CAPTCHAAI_CONCURRENCY diset ke 20 tapi paket yang dibeli cuma STANDARD (15 thread), lima task tambahan antre menunggu thread kosong — bukan gagal, tapi throughput tidak pernah menyentuh 20. Aturan praktis: samakan CAPTCHAAI_CONCURRENCY dengan thread count paket Anda, lalu naikkan keduanya bersamaan saat volume bertambah.
Untuk tim yang deploy worker di AWS ap-southeast-1 (Singapura), ap-southeast-3 (Jakarta), atau GCP asia-southeast2 (Jakarta), latensi ke endpoint CaptchaAI biasanya sudah rendah sehingga default CAPTCHAAI_POLL_INTERVAL (5 detik) jarang perlu diubah — yang lebih sering disesuaikan adalah CAPTCHAAI_TIMEOUT, terutama saat jaringan mobile pengguna akhir ikut memengaruhi waktu proses.
Config Loader Siap Pakai
Python
Loader ini menerapkan urutan prioritas dari atas: baca file YAML dulu, timpa dengan env variable, baru validasi. CaptchaAIConfig.load() melempar ValueError kalau api_key kosong, jadi kegagalan konfigurasi ketahuan saat startup — bukan di tengah batch solve.
import os
import yaml
from dataclasses import dataclass, field
from pathlib import Path
@dataclass
class CaptchaAIConfig:
api_key: str = ""
submit_url: str = "https://ocr.captchaai.com/in.php"
poll_url: str = "https://ocr.captchaai.com/res.php"
poll_interval: int = 5
max_polls: int = 60
concurrency: int = 10
timeout: int = 300
proxy: str = ""
callback_url: str = ""
retries: int = 3
log_level: str = "info"
@classmethod
def load(cls, config_path=None):
"""Load config: env vars override file, which overrides defaults."""
config = cls()
# Layer 2: Config file
if config_path and Path(config_path).exists():
with open(config_path) as f:
file_config = yaml.safe_load(f) or {}
for key, value in file_config.items():
if hasattr(config, key):
setattr(config, key, value)
# Layer 1: Environment variables (highest priority)
env_map = {
"CAPTCHAAI_API_KEY": "api_key",
"CAPTCHAAI_SUBMIT_URL": "submit_url",
"CAPTCHAAI_POLL_URL": "poll_url",
"CAPTCHAAI_POLL_INTERVAL": "poll_interval",
"CAPTCHAAI_MAX_POLLS": "max_polls",
"CAPTCHAAI_CONCURRENCY": "concurrency",
"CAPTCHAAI_TIMEOUT": "timeout",
"CAPTCHAAI_PROXY": "proxy",
"CAPTCHAAI_CALLBACK_URL": "callback_url",
"CAPTCHAAI_RETRIES": "retries",
"CAPTCHAAI_LOG_LEVEL": "log_level",
}
for env_key, attr_name in env_map.items():
value = os.environ.get(env_key)
if value is not None:
# Cast to correct type
current = getattr(config, attr_name)
if isinstance(current, int):
value = int(value)
setattr(config, attr_name, value)
config.validate()
return config
def validate(self):
if not self.api_key:
raise ValueError("CAPTCHAAI_API_KEY is required")
if self.poll_interval < 1:
raise ValueError("poll_interval must be >= 1")
if self.concurrency < 1:
raise ValueError("concurrency must be >= 1")
# Usage
config = CaptchaAIConfig.load("config/captchaai.yaml")
print(f"Concurrency: {config.concurrency}, Timeout: {config.timeout}s")
JavaScript / Node.js
Versi Node.js memakai pola yang sama — default, file config, lalu env variable — dengan validasi eksplisit sebelum dipakai worker:
const fs = require("fs");
const yaml = require("js-yaml");
const path = require("path");
class CaptchaAIConfig {
static defaults = {
apiKey: "",
submitUrl: "https://ocr.captchaai.com/in.php",
pollUrl: "https://ocr.captchaai.com/res.php",
pollInterval: 5,
maxPolls: 60,
concurrency: 10,
timeout: 300,
proxy: "",
callbackUrl: "",
retries: 3,
logLevel: "info",
};
static envMap = {
CAPTCHAAI_API_KEY: "apiKey",
CAPTCHAAI_SUBMIT_URL: "submitUrl",
CAPTCHAAI_POLL_URL: "pollUrl",
CAPTCHAAI_POLL_INTERVAL: { key: "pollInterval", type: "int" },
CAPTCHAAI_MAX_POLLS: { key: "maxPolls", type: "int" },
CAPTCHAAI_CONCURRENCY: { key: "concurrency", type: "int" },
CAPTCHAAI_TIMEOUT: { key: "timeout", type: "int" },
CAPTCHAAI_PROXY: "proxy",
CAPTCHAAI_CALLBACK_URL: "callbackUrl",
CAPTCHAAI_RETRIES: { key: "retries", type: "int" },
CAPTCHAAI_LOG_LEVEL: "logLevel",
};
static load(configPath = null) {
let config = { ...CaptchaAIConfig.defaults };
// Layer 2: Config file
if (configPath && fs.existsSync(configPath)) {
const ext = path.extname(configPath);
const raw = fs.readFileSync(configPath, "utf8");
const fileConfig = ext === ".json" ? JSON.parse(raw) : yaml.load(raw);
config = { ...config, ...fileConfig };
}
// Layer 1: Environment variables
for (const [envKey, mapping] of Object.entries(CaptchaAIConfig.envMap)) {
const value = process.env[envKey];
if (value !== undefined) {
const attrKey = typeof mapping === "string" ? mapping : mapping.key;
const type = typeof mapping === "string" ? "string" : mapping.type;
config[attrKey] = type === "int" ? parseInt(value, 10) : value;
}
}
CaptchaAIConfig.validate(config);
return config;
}
static validate(config) {
if (!config.apiKey) throw new Error("CAPTCHAAI_API_KEY is required");
if (config.pollInterval < 1) throw new Error("pollInterval must be >= 1");
if (config.concurrency < 1) throw new Error("concurrency must be >= 1");
}
}
// Usage
const config = CaptchaAIConfig.load("config/captchaai.yaml");
console.log(`Concurrency: ${config.concurrency}, Timeout: ${config.timeout}s`);
Config Terpisah per Lingkungan: Base, Staging, Production
Simpan nilai yang sama di semua lingkungan di file base, lalu override per lingkungan lewat file terpisah. Staging pakai concurrency rendah dan log level debug supaya gampang ditelusuri; production menaikkan concurrency, mempersingkat poll interval, dan menurunkan log level ke warning:
# config/captchaai.yaml — base
api_key: "" # Selalu set via env var
concurrency: 5
poll_interval: 5
retries: 3
log_level: info
# config/captchaai.production.yaml
concurrency: 20
poll_interval: 3
timeout: 180
log_level: warning
# config/captchaai.staging.yaml
concurrency: 3
poll_interval: 5
timeout: 300
log_level: debug
API key tidak pernah muncul di ketiga file ini — nilainya datang dari env variable, sesuai urutan prioritas di awal panduan.
Menyimpan Secret dengan Aman di Production
API key CaptchaAI tidak boleh ada di file konfigurasi maupun version control. Pilih metode sesuai infrastruktur yang sudah Anda pakai:
| Metode | Terbaik untuk | Contoh |
|---|---|---|
| Environment variable | Container, CI/CD | export CAPTCHAAI_API_KEY=abc123 |
| AWS Secrets Manager | Infrastruktur AWS | Ambil saat startup; rotasi otomatis |
| HashiCorp Vault | Multi-cloud, on-prem | Dynamic secret dengan TTL |
| Docker Secrets | Docker Swarm/Compose | Di-mount di /run/secrets/ |
File .env (dev only) |
Development lokal | Library dotenv; tambahkan ke .gitignore |
Contoh berikut menyuntikkan API key lewat env variable dan file .env.production yang di-gitignore — pola umum untuk tim yang sudah pakai Docker Compose:
services:
captcha-worker:
image: captcha-worker:latest
environment:
- CAPTCHAAI_API_KEY=${CAPTCHAAI_API_KEY}
- CAPTCHAAI_CONCURRENCY=15
- CAPTCHAAI_LOG_LEVEL=warning
env_file:
- .env.production
Kalau task CAPTCHA menyentuh data pengguna, filter juga log config-dump dari data pribadi — relevan dengan UU Pelindungan Data Pribadi (UU 27/2022), bukan cuma soal API key yang bocor.
Feature Flag: Ubah Perilaku Tanpa Deploy Ulang
Feature flag memisahkan "deploy kode baru" dari "nyalakan perilaku baru". Baca flag dari env variable di setiap request, bukan cuma saat startup, supaya fitur bermasalah bisa dimatikan tanpa build ulang:
class FeatureFlags:
def __init__(self):
self.flags = {
"use_callback": os.environ.get("FF_USE_CALLBACK", "false") == "true",
"enable_proxy": os.environ.get("FF_ENABLE_PROXY", "true") == "true",
"max_concurrent": int(os.environ.get("FF_MAX_CONCURRENT", "10")),
}
def is_enabled(self, flag):
return self.flags.get(flag, False)
def get(self, flag, default=None):
return self.flags.get(flag, default)
Masalah Konfigurasi yang Sering Muncul
Empat masalah ini paling sering muncul saat pindah dari dev ke production:
| Masalah | Penyebab | Solusi |
|---|---|---|
| API key tidak termuat | Env var tidak ada; nama variabel salah | Cek echo $CAPTCHAAI_API_KEY; verifikasi ejaan |
| File konfigurasi diabaikan | Path salah atau library YAML tidak ada | Verifikasi file ada; install pyyaml / js-yaml |
| Production menggunakan pengaturan dev | Override spesifik lingkungan tidak diterapkan | Cek prioritas env var; verifikasi NODE_ENV / APP_ENV |
| Secret terlihat di log | Config dump menyertakan API key | Mask field sensitif dalam output log |
Pertanyaan Umum
YAML atau JSON — mana yang cocok untuk config CaptchaAI?
YAML kalau file-nya diedit manusia dan butuh komentar penjelasan tiap baris. JSON kalau config di-generate otomatis atau Anda butuh parsing ketat tanpa ambiguitas indentasi. Loader Python di atas menerima keduanya, asal library pyyaml atau parser JSON bawaan sudah terpasang.
Apakah CAPTCHAAI_CONCURRENCY harus sama dengan jumlah thread di paket saya?
Idealnya ya. Set concurrency sama dengan thread yang dibayar — misalnya 50 untuk ADVANCE — supaya kapasitas terpakai penuh tanpa task menumpuk menunggu thread kosong.
Amankah menyimpan CAPTCHAAI_API_KEY di file .env yang masuk repository privat?
Tidak disarankan, meski repo-nya privat. History Git menyimpan setiap commit selamanya, dan akses repo sering lebih longgar dari akses secret manager. Pakai .env hanya untuk development lokal; untuk production, ambil API key dari secret manager saat startup.
Perlukah CAPTCHAAI_TIMEOUT dan CAPTCHAAI_POLL_INTERVAL berbeda antara staging dan production?
Biasanya ya. Staging pakai timeout longgar dan log level debug karena prioritasnya observabilitas. Production menurunkan poll interval dan menaikkan concurrency karena prioritasnya throughput — lihat contoh captchaai.production.yaml dan captchaai.staging.yaml di atas.
Bisakah saya mengubah concurrency tanpa restart service?
Bisa — asal aplikasi membaca env variable atau config service di setiap batch task, bukan cuma saat startup. Update env var lalu kirim sinyal reload ke proses; tidak perlu redeploy.
Artikel Terkait
Susun config production Anda sekarang — ambil API key CaptchaAI dan mulai dari template di atas.