Ada tiga aturan wajib saat encode base64 image CAPTCHA untuk API CaptchaAI: kirim raw base64 tanpa prefix data URI, jangan encode dua kali, dan selalu baca file gambar sebagai bytes (rb), bukan teks. Lewatkan salah satu saja, dan hasilnya bisa berupa error langsung seperti ERROR_WRONG_FILE_EXTENSION — atau yang lebih menyebalkan, task yang lolos validasi tapi solve-nya meleset karena gambar sudah rusak sebelum sampai ke server CaptchaAI. Panduan ini membahas empat cara encode base64 (dari file, URL, dan dua pendekatan screenshot Selenium), tiga kesalahan encoding paling umum di lapangan, plus checklist validasi yang bisa Anda pasang sebelum setiap request ke in.php.
Format Base64 yang Diterima API CaptchaAI
CaptchaAI menerima gambar CAPTCHA dalam bentuk string base64 lewat parameter method=base64 pada endpoint in.php. Anda tidak perlu upload file secara langsung — cukup encode gambar di sisi Anda, lalu kirim base64 murni sebagai nilai body. Fungsi berikut adalah pola dasar yang dipakai di semua contoh pada panduan ini: encode dulu secara lokal, submit lewat requests.post, lalu tangani respons JSON dari CaptchaAI.
import requests
import base64
import os
def submit_image_captcha(image_base64):
"""Submit base64-encoded image to CaptchaAI."""
resp = requests.post("https://ocr.captchaai.com/in.php", data={
"key": os.environ["CAPTCHAAI_API_KEY"],
"method": "base64",
"body": image_base64,
"json": 1,
}, timeout=30)
return resp.json()
Simpan CAPTCHAAI_API_KEY di environment variable, bukan hard-code di skrip — kebiasaan yang sama juga sebaiknya berlaku untuk kredensial lain di pipeline scraping atau automation Anda.
Cara Encode Gambar CAPTCHA dari File Lokal
Kasus paling sederhana: Anda sudah punya file gambar CAPTCHA di disk — hasil download manual, dataset uji, atau screenshot yang disimpan oleh proses sebelumnya. Fungsi encode_from_file di bawah membaca file dalam mode binary (rb) dan mengembalikan base64 sebagai string ASCII, siap dipakai sebagai nilai body pada request ke in.php.
# from_file.py
import base64
def encode_from_file(filepath):
"""Read an image file and return base64 string."""
with open(filepath, "rb") as f:
raw = f.read()
return base64.b64encode(raw).decode("ascii")
# Usage
b64 = encode_from_file("captcha.png")
print(f"Encoded length: {len(b64)} chars")
Cetak len(b64) untuk sanity check cepat — string yang jauh lebih pendek dari biasanya menandakan file kosong atau download yang terputus.
Cara Encode Gambar CAPTCHA Langsung dari URL
Kalau CAPTCHA muncul sebagai gambar yang di-hosting di URL publik — bukan elemen halaman yang perlu di-screenshot — Anda bisa download lalu encode dalam satu langkah. Pengecekan Content-Type di bawah ini penting: banyak endpoint yang diblokir atau kena rate limit tetap mengembalikan status 200 berisi halaman HTML error, bukan gambar, dan itu akan lolos ke tahap encode kalau tidak divalidasi lebih dulu.
# from_url.py
import requests
import base64
def encode_from_url(image_url):
"""Download image and return base64 string."""
resp = requests.get(image_url, timeout=15)
resp.raise_for_status()
# Verify it's actually an image
content_type = resp.headers.get("Content-Type", "")
if not content_type.startswith("image/"):
raise ValueError(f"Not an image: {content_type}")
return base64.b64encode(resp.content).decode("ascii")
# Usage
b64 = encode_from_url("https://example.com/captcha.png")
Timeout 15 detik pada contoh ini cukup untuk sebagian besar CDN gambar. Naikkan sedikit kalau worker Anda berjalan di jaringan dengan latensi lebih tinggi ke origin server.
Cara Encode Screenshot Selenium ke Base64
Skenario yang sering muncul di tim price-monitoring atau data scraping Indonesia: worker Selenium jalan di server AWS ap-southeast-1 (Singapura) atau GCP asia-southeast2 (Jakarta), ambil screenshot elemen CAPTCHA dari halaman checkout, langsung encode ke base64 tanpa menyentuh disk. Ini lebih murah dibanding simpan-ke-file dulu, terutama kalau worker jalan dalam jumlah banyak.
# from_selenium.py
import base64
from selenium.webdriver.common.by import By
def encode_from_element(driver, selector):
"""Screenshot a specific element and return base64."""
element = driver.find_element(By.CSS_SELECTOR, selector)
screenshot_b64 = element.screenshot_as_base64
return screenshot_b64
def encode_from_page_crop(driver, selector):
"""Crop a specific region from the page screenshot."""
from PIL import Image
import io
element = driver.find_element(By.CSS_SELECTOR, selector)
location = element.location
size = element.size
# Full page screenshot
png = driver.get_screenshot_as_png()
img = Image.open(io.BytesIO(png))
# Crop to element bounds
left = location["x"]
top = location["y"]
right = left + size["width"]
bottom = top + size["height"]
cropped = img.crop((left, top, right, bottom))
# Encode
buffer = io.BytesIO()
cropped.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode("ascii")
Pakai encode_from_element kapan pun bisa — sudah mengembalikan base64 langsung, tanpa langkah tambahan. encode_from_page_crop baru diperlukan kalau elemen CAPTCHA tidak bisa di-screenshot langsung (misalnya di dalam iframe lintas origin), sehingga Anda harus crop koordinatnya sendiri dengan Pillow.
Kesalahan Encoding Base64 yang Paling Sering Terjadi
Tiga pola error ini yang paling sering bikin task base64 gagal atau salah solve, berdasarkan pola yang berulang di kode integrasi:
Kesalahan 1: Prefix Data URI Ikut Terkirim
Kalau gambar diambil dari <img src> atau canvas lewat JavaScript, hasilnya sering berupa data URI lengkap (data:image/png;base64,...), bukan base64 murni. CaptchaAI hanya menerima bagian setelah koma — kirim prefix-nya dan Anda dapat ERROR_WRONG_FILE_EXTENSION karena server tidak bisa mendeteksi format gambarnya.
# SALAH — menyertakan data URI prefix
bad = "data:image/png;base64,iVBORw0KGgo..."
# BENAR — raw base64 saja
good = "iVBORw0KGgo..."
# Fix: Hapus prefix-nya
def clean_base64(b64_string):
if "," in b64_string:
return b64_string.split(",", 1)[1]
return b64_string
Kesalahan 2: Encoding Ganda (Double Encoding)
screenshot_as_base64 milik Selenium sudah mengembalikan base64, bukan bytes mentah. Meng-encode ulang hasil itu — mudah lolos code review karena kodenya terlihat masuk akal — menghasilkan base64-dari-base64 yang tidak bisa didekode balik jadi gambar valid.
# SALAH — encode string yang sudah di-encode
already_b64 = element.screenshot_as_base64
double_encoded = base64.b64encode(already_b64.encode()).decode() # SALAH
# BENAR — gunakan langsung
correct = element.screenshot_as_base64 # Sudah base64
Kesalahan 3: Baca File sebagai Teks, Bukan Bytes
Membuka file gambar dengan mode "r" alih-alih "rb" membuat Python mencoba decode byte gambar sebagai teks, lalu re-encode dengan encoding default sistem. Hasilnya data biner yang rusak sebelum sempat di-base64-kan — kesalahan klasik developer yang baru pindah dari file teks ke file biner.
# SALAH — membaca sebagai teks
with open("captcha.png", "r") as f: # Text mode
content = f.read() # Binary data rusak
# BENAR — membaca sebagai bytes
with open("captcha.png", "rb") as f: # Binary mode
content = f.read()
encoded = base64.b64encode(content).decode("ascii")
Validasi Base64 Sebelum Dikirim ke API
CaptchaAI menagih per thread aktif, bukan per solve — jadi base64 yang gagal validasi tetap memakan slot thread sampai timeout atau server menolaknya. Untuk tim yang cost-sensitive (agensi price-monitoring, tim scraping freelance), validasi lokal seperti di bawah ini lebih murah daripada menunggu error balik dari in.php.
# validate.py
import base64
import io
def validate_captcha_image(b64_string):
"""Validate base64 image before submitting to CaptchaAI."""
errors = []
# Check for data URI prefix
if b64_string.startswith("data:"):
errors.append("Contains data URI prefix — strip it")
b64_string = b64_string.split(",", 1)[1]
# Try decoding
try:
decoded = base64.b64decode(b64_string)
except Exception as e:
return {"valid": False, "errors": [f"Invalid base64: {e}"]}
# Check size
size_kb = len(decoded) / 1024
if size_kb < 1:
errors.append(f"Image too small ({size_kb:.1f} KB) — likely corrupt")
if size_kb > 500:
errors.append(f"Image large ({size_kb:.1f} KB) — consider resizing")
# Check image format
if decoded[:8] == b'\x89PNG\r\n\x1a\n':
fmt = "PNG"
elif decoded[:3] == b'\xff\xd8\xff':
fmt = "JPEG"
elif decoded[:4] == b'GIF8':
fmt = "GIF"
elif decoded[:4] == b'RIFF':
fmt = "WEBP"
else:
errors.append("Unknown image format")
fmt = "unknown"
return {
"valid": len(errors) == 0,
"format": fmt,
"size_kb": round(size_kb, 1),
"errors": errors,
}
# Usage
result = validate_captcha_image(b64_string)
if not result["valid"]:
print(f"Issues: {result['errors']}")
else:
print(f"Valid {result['format']}, {result['size_kb']} KB")
Jalankan validate_captcha_image() sebagai langkah terakhir sebelum submit_image_captcha(). Kalau result["valid"] bernilai False, perbaiki dulu di sisi Anda daripada menunggu balasan error dari server.
Format Gambar Mana yang Paling Cocok untuk CAPTCHA Anda?
Format gambar memengaruhi akurasi solve, bukan cuma ukuran file yang dikirim. Ringkasannya:
| Format | Cocok Untuk | Ukuran File | Kualitas |
|---|---|---|---|
| PNG | CAPTCHA teks, screenshot | Lebih besar | Lossless |
| JPEG | CAPTCHA berbasis foto | Lebih kecil | Lossy (gunakan kualitas ≥ 85) |
| GIF | CAPTCHA animasi | Variabel | Warna terbatas |
| WebP | Browser modern | Terkecil | Kualitas baik |
Rekomendasi: Untuk CAPTCHA berbasis teks, PNG tetap pilihan paling aman — kompresi lossless-nya menjaga tepian karakter tetap tajam, dan itu langsung berpengaruh ke akurasi hasil solve.
Solusi Error Encoding yang Sering Muncul
Kalau base64 sudah lolos validasi tapi API tetap menolak, cek tabel berikut dulu sebelum mengubah kode lain yang tidak relevan:
| Masalah | Penyebab | Solusi |
|---|---|---|
ERROR_WRONG_FILE_EXTENSION |
Data base64 tidak valid | Validasi dengan validate_captcha_image() |
ERROR_TOO_BIG_CAPTCHA_FILESIZE |
Gambar melebihi 600 KB | Resize atau compress sebelum encode |
ERROR_ZERO_CAPTCHA_FILESIZE |
Gambar kosong atau rusak | Periksa download berhasil |
| Hasil solve salah | JPEG terlalu terkompresi | Gunakan PNG atau JPEG kualitas ≥ 85 |
Batas 600 KB berlaku untuk body base64, bukan file gambar sebelum di-encode — base64 menambah ukuran sekitar 33%, jadi gambar mentah idealnya di bawah ~450 KB.
Pertanyaan yang Sering Ditanyakan
Berapa ukuran maksimum gambar yang bisa dikirim sebagai base64?
600 KB untuk body base64 itu sendiri. Karena base64 menambah ukuran sekitar 33%, gambar mentah sebaiknya di bawah 450 KB — resize screenshot besar sebelum di-encode.
Apakah CaptchaAI menerima format WebP?
Ya, WebP termasuk format yang diterima dan menghasilkan ukuran file terkecil di antara format umum. Untuk CAPTCHA berbasis teks, PNG tetap lebih disarankan karena kompresinya lossless dan mempertahankan tepian karakter.
Kenapa hasil solve masih salah walau base64 sudah lolos validasi?
Validasi hanya memastikan base64 valid dan bisa didekode menjadi gambar — bukan menjamin gambarnya jelas. Penyebab paling umum: kompresi JPEG yang terlalu agresif (gunakan kualitas ≥ 85) atau crop screenshot yang memotong sebagian karakter CAPTCHA.
Perlukah saya resize screenshot sebelum encode kalau worker berjalan di jaringan terbatas?
Ya. Screenshot penuh halaman bisa mencapai beberapa ratus KB; crop ke elemen CAPTCHA saja (lihat encode_from_element di atas) memangkas payload dan mempercepat request ke in.php.
Bisakah saya submit gambar SVG langsung?
Tidak. SVG adalah format vektor, bukan raster, sementara API base64 CaptchaAI mengharapkan data piksel. Konversi ke PNG dulu memakai library seperti Pillow atau cairosvg sebelum encode.