Authentication
More
Every call your code makes carries an API key. The key says which account the call is for and what it may do. You create keys in the dashboard; see API keys for scopes, allowed addresses, spend caps and revocation.
Send the key
Section titled “Send the key”| Format | Where the key goes |
|---|---|
| REST v1 | Authorization: Bearer zc_live_… |
| createTask format | clientKey in the JSON body |
| 2Captcha format | the key parameter |
The API is at https://api.zerocaptcha.io. Read the key and the address from your environment, never from your
source code. The samples in these docs use ZEROCAPTCHA_KEY and ZEROCAPTCHA_API:
export ZEROCAPTCHA_KEY="$(cat /run/secrets/zerocaptcha_key)" # or from your secret managercurl "$ZEROCAPTCHA_API/v1/balance" -H "Authorization: Bearer $ZEROCAPTCHA_KEY"const key = process.env.ZEROCAPTCHA_KEY;if (!key) throw new Error("Set ZEROCAPTCHA_KEY");
const response = await fetch(`${process.env.ZEROCAPTCHA_API}/v1/balance`, { headers: { Authorization: `Bearer ${key}` },});console.log(response.status, await response.json());import os
import requests
key = os.environ["ZEROCAPTCHA_KEY"]response = requests.get( f"{os.environ['ZEROCAPTCHA_API']}/v1/balance", headers={"Authorization": f"Bearer {key}"}, timeout=15,)print(response.status_code, response.json())package main
import ( "fmt" "io" "net/http" "os")
func main() { req, _ := http.NewRequest(http.MethodGet, os.Getenv("ZEROCAPTCHA_API")+"/v1/balance", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("ZEROCAPTCHA_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(resp.StatusCode, string(body))}A key needs no cookie and no CSRF token: those belong to the dashboard’s own session. A key can create and read tasks and read the balance; managing keys, billing and the team takes a signed-in dashboard session, so a leaked key can never create another key.
What a key looks like
Section titled “What a key looks like”A key is 41 characters: zc_live_, 27 random characters, then a 6-character checksum. The prefix
makes a leaked key easy to spot, and the checksum lets a secret scanner confirm a match without
calling us. There is one kind of key: every task it creates is real, and charged if it succeeds.
A wrong, mistyped or unknown key is refused with
unauthorized (HTTP 401), or
ERROR_KEY_DOES_NOT_EXIST in the createTask
format. A revoked key is refused with key_revoked.
Rotate without an outage
Section titled “Rotate without an outage”- On the dashboard’s API keys page, choose Rotate on the key and an overlap: 1 hour, 24 hours or 7 days. The new key is shown once; copy it into your secret store.
- Deploy it. During the overlap both keys work.
- When every service uses the new key, end the overlap, or let it run out. The old key then
answers
key_revoked.
Rotate on a schedule, when someone who had the key leaves, and at once if it may have leaked. API keys has the details.
Key hygiene
Section titled “Key hygiene”- Keep keys in a secret store or the environment, never in source code, a repository, a container image, a screenshot or a support message. Support never needs your key.
- Never send a key to a browser. Call ZeroCaptcha from your server, and hand the browser only the result it needs. A key in front-end code or a mobile app can be read by anyone.
- Never log it. Leave the
Authorizationheader out of request logs and error reports. - Use one key per service or environment, named in the dashboard, so you can rotate or revoke one without touching the others, and see which one made a task.
- Give each key only what it needs. A reporting job needs only the
balancescope. - Hold a key to your servers’ addresses with its IP allowlist, so a leaked key is useless elsewhere.
- Cap a key’s daily spend, so a runaway loop or a leak costs at most the cap.
- Watch it. The keys page shows when each key was last used and from where.
If a key leaks, revoke it on the keys page: it stops at once, and tasks it already created run to their end. Then create a new one.