Documentation

API Reference

Integrate Peak's CAPTCHA solving API in minutes. All endpoints return JSON over HTTPS. You are only charged for successful solves.

Base URLhttps://api.peak.fo

Introduction

The Peak API is a REST API that accepts and returns JSON. Every request is authenticated with an API key. Billing is applied per successful solve only — failed or errored requests are never charged.

Billing

Success only

Format

JSON / HTTPS

Auth

API key

Authentication

Pass your API key via the X-API-Key header on every request.

headers = {
    "X-API-Key": "pk_your_api_key",
    "Content-Type": "application/json"
}
Keep your key secret. Never expose it in client-side code or commit it to version control. Rotate from the dashboard if compromised.
POST /solve

Cloudflare Turnstile

Solves Cloudflare Turnstile widgets and returns a token you submit as the cf-turnstile-response field. Average response time is under 1 second.

POST/solve
ParameterTypeRequiredDescription
task_typestringyesMust be "turnstiletask"
sitekeystringyesCloudflare Turnstile sitekey for the target site
urlstringyesTarget page URL — must end with a trailing slash (/)
proxystringyesProxy in http://user:pass@ip:port format
actionstringnoTurnstile action value — must match the value configured on the target site
cdatastringnoTurnstile cdata value — must match the value configured on the target site
The URL must end with a trailing slash: https://example.com/

Request

{
  "task_type": "turnstiletask",
  "url": "https://example.com/",
  "sitekey": "0x4AAAAAAAxxxxx",
  "proxy": "http://user:pass@1.2.3.4:8080"
}

Response

{
  "success": true,
  "data": {
    "token": "0.AgAAABBqzz..."
  }
}
import requests

url = "https://api.peak.fo/solve"
headers = {
    "X-API-Key": "pk_your_api_key",
    "Content-Type": "application/json"
}
payload = {
    "task_type": "turnstiletask",
    "sitekey": "0x4AAAAAAA...",
    "url": "https://example.com/",
    "proxy": "http://user:pass@1.2.3.4:8080"
}

response = requests.post(url, json=payload, headers=headers)
data = response.json()

if data["success"]:
    token = data["data"]["token"]
    print(f"Token: {token}")
else:
    print(f"Error: {data['error']}")
POST /solve

Cloudflare 5s Challenge

Solve Cloudflare's browser integrity check. Returns clearance cookies, a matching user-agent, and form attributes needed to complete the challenge on the target site.

POST/solve
ParameterTypeRequiredDescription
task_typestringyesMust be "cloudflare5stask"
urlstringyesTarget page URL — must end with a trailing slash (/)
proxystringyesSticky/session proxy in http://user:pass@ip:port format
user_agentstringnoWindows Chrome user-agent (only Windows Chrome is supported)
htmlstringnoPre-fetched page HTML — optional optimisation to skip an initial fetch
Sticky proxies required. Cloudflare validates that the same IP is used throughout the session. Each new session needs a fresh proxy session.

Request

{
  "task_type": "cloudflare5stask",
  "url": "https://example.com/",
  "proxy": "http://user:pass@1.2.3.4:8080"
}

Response

{
  "success": true,
  "data": {
    "cookies": {
      "cf_clearance": "xxx",
      "__cf_bm": "xxx"
    },
    "headers": {
      "user-agent": "Mozilla/5.0..."
    },
    "attributes": {
      "md": "xxx",
      "r": "xxx"
    },
    "cf_rt": "challenge_token"
  }
}

Using the response — 4 steps

1Apply the cookies from the response to your HTTP session.
2POST the attributes as form-encoded body to the target URL with Content-Type: application/x-www-form-urlencoded.
3Set the Referer header to {url}?__cf_chl_tk={cf_rt}.
4Use the exact user-agent from the response for all subsequent requests.
import requests

TARGET_URL = "https://example.com/"
PROXY = "http://user:pass@1.2.3.4:8080"  # Sticky session proxy

# Step 1: Get WAF solution from Peak API
response = requests.post(
    "https://api.peak.fo/solve",
    headers={
        "X-API-Key": "pk_your_api_key",
        "Content-Type": "application/json"
    },
    json={
        "task_type": "cloudflare5stask",
        "url": TARGET_URL,
        "proxy": PROXY
    }
)

data = response.json()
if not data["success"]:
    raise Exception(f"Solve failed: {data['error']}")

solution = data["data"]
clearance = solution["cookies"]["cf_clearance"]
cf_bm = solution["cookies"].get("__cf_bm", "")
cf_rt = solution["cf_rt"]
attributes = solution["attributes"]
user_agent = solution["headers"]["user-agent"]

# Step 2: Build attributes as form data
form_data = "&".join([f"{k}={v}" for k, v in attributes.items()])

# Step 3: Create session with cookies
session = requests.Session()
session.cookies.set("cf_clearance", clearance, domain="example.com")
if cf_bm:
    session.cookies.set("__cf_bm", cf_bm, domain="example.com")

# Step 4: POST to complete the challenge
resp = session.post(
    TARGET_URL,
    data=form_data,
    headers={
        "User-Agent": user_agent,
        "Referer": f"{TARGET_URL}?__cf_chl_tk={cf_rt}",
        "Content-Type": "application/x-www-form-urlencoded",
    },
    proxies={"http": PROXY, "https": PROXY}
)

print(f"Status: {resp.status_code}")
# Now you can make requests with the session cookies
POST /solve

AWS WAF

Generates an AWS WAF challenge token from the mobile SDK. Attach the token to subsequent requests as a header or cookie.

Mobile SDK only. This solver is designed for AWS WAF challenges in iOS/Android mobile apps — not web browsers. You need the sdk_url extracted from the mobile app binary.
POST/solve
ParameterTypeRequiredDescription
task_typestringyesMust be "awswaftask"
urlstringyesTarget application domain URL
sdk_urlstringyesAWS WAF SDK endpoint extracted from the mobile app (see below)
proxystringyesProxy in http://user:pass@ip:port format
user_agentstringnoMobile app user-agent
osstringno"ios" or "android" — auto-detected from user_agent if omitted

Finding the SDK URL

The sdk_url is hardcoded in the mobile app and matches this pattern:

https://xxxxx.edge.sdk.awswaf.com/xxxxx/xxxxx/challenge.js
HTTP proxyIntercept app traffic with Charles Proxy or mitmproxy and look for requests matching the pattern above.
DecompilationDecompile the APK/IPA and search for the awswaf.com URL string.
Pattern matchSearch for "edge.sdk.awswaf.com" in the app binary or network logs.

Request

{
  "task_type": "awswaftask",
  "url": "https://app.example.com/",
  "sdk_url": "https://xxxxx.edge.sdk.awswaf.com/xxxxx/xxxxx",
  "proxy": "http://user:pass@1.2.3.4:8080",
  "os": "android"
}

Response

{
  "success": true,
  "data": {
    "token": "eyJxxxxxxxxxxxxxx..."
  }
}
Attach the token as the x-aws-waf-token header, or as the aws-waf-token cookie — whichever the target app expects.
import requests

# Get token from Peak API
response = requests.post(
    "https://api.peak.fo/solve",
    headers={
        "X-API-Key": "pk_your_api_key",
        "Content-Type": "application/json"
    },
    json={
        "task_type": "awswaftask",
        "url": "https://app.example.com/",
        "sdk_url": "https://xxxxx.edge.sdk.awswaf.com/xxxxx/xxxxx",
        "proxy": "http://user:pass@1.2.3.4:8080",
        "user_agent": "AppName/1.0 (iOS 17.0)"
    }
)

data = response.json()
if data["success"]:
    token = data["data"]["token"]
    print(f"Token: {token}")

    # Use token in your requests:
    # Header: x-aws-waf-token: {token}
    # Cookie: aws-waf-token={token}
else:
    print(f"Error: {data['error']}")
GET /balance

Balance

Returns the current balance or package status for the authenticated API key. Response shape differs by billing type.

GET/balance

Pay-per-solve key

{
  "success": true,
  "data": {
    "type": "pay_per_solve",
    "balance": 12.5
  }
}

Package key

{
  "success": true,
  "data": {
    "type": "package",
    "package_name": "Turnstile 500K",
    "remaining_solves": 342819,
    "total_solves": 500000,
    "allowed_tasks": [
      "turnstiletask"
    ],
    "expires_at": "2026-06-25T00:00:00Z",
    "days_remaining": 30
  }
}

Error Codes

All errors return {"success":false,"error":"..."}. A 200 with success: false means a non-fatal solve failure — not billed.

HTTP StatusMeaning
200 (failed)Solve failed — error field explains why. Not billed.
400Bad request — missing or invalid fields.
401Unauthorized — API key missing, invalid, or expired.
402Payment required — insufficient balance or package exhausted.
503Service unavailable — task type disabled or no solver capacity.
Error messageFix
missing API keyAdd X-API-Key header to your request.
insufficient balanceTop up your balance or purchase a package.
package solves exhaustedPackage fully consumed — purchase a new one.
Proxy connection failedCheck proxy format: http://user:pass@ip:port and verify it's reachable.
Request timed outUse a faster proxy closer to the target server.
IP address is blocked by CloudFlareSwitch to a proxy with a cleaner IP reputation.
No WAF challenge detectedTarget page has no active challenge, or the URL is wrong.
Invalid sitekeyVerify the sitekey matches the domain — it's domain-scoped.
Turnstile challenge was flaggedUse a proxy with a cleaner IP reputation.
Unsupported user agentOnly Windows Chrome user-agents are supported for WAF tasks.