Grab 1,000 free solves. No card required.Start free

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

Turnstile (Proxyless)

The same Turnstile solve without supplying a proxy. Send TurnstileTaskProxyLess and Peak runs it through its own pool, returning the same token. Billed at $1.20 / 1,000.

POST/solve
ParameterTypeRequiredDescription
task_typestringyesMust be "TurnstileTaskProxyLess"
sitekeystringyesCloudflare Turnstile sitekey for the target site
urlstringyesTarget page URL — must end with a trailing slash (/)
actionstringnoTurnstile action value — must match the value configured on the target site
cdatastringnoTurnstile cdata value — must match the value configured on the target site
Best for sites that accept the token from any IP. If a target only accepts the token from the IP that solved it, use the proxied turnstiletask with your own proxy instead. This mode is new and still stabilizing.

Request

{
  "task_type": "TurnstileTaskProxyLess",
  "url": "https://example.com/",
  "sitekey": "0x4AAAAAAAxxxxx"
}

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": "TurnstileTaskProxyLess",
    "sitekey": "0x4AAAAAAA...",
    "url": "https://example.com/"
    # no proxy — Peak solves through its own pool
}

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
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
  }
}

Referrals & app IDs

Two ways to earn solve credit on top of normal use. Both pay into your balance, there is no cap, and neither needs a payout — you spend what you earn on your own solving.

Referrals

Share your link from Dashboard → Referrals. Anyone who signs up through it is tied to your account, and you earn 20% of everything they spend on solves. Links look like https://peak.fo/?ref=YOURCODE.

App IDs

Building a tool, SDK, or bot on Peak? Create an app ID at Dashboard → Developer and add it to the solve payload as appId. Every solve that carries it credits you 5% of the cost — whether the call comes from you or from someone using your integration. The field is optional and ignored when absent, so it never affects a solve.

{
  "task_type": "TurnstileTaskProxyLess",
  "url": "https://example.com",
  "sitekey": "0x4AAAAAAA...",
  "appId": "app_your_app_id"
}

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.