API reference

Two APIs behind one key: tmpt mints anti-bot tokens for a target site, BotDeflect solves queue-it challenges. Both are plain JSON over HTTPS.

Base URL https://api.antibot.primeseats.dev

Base URL & auth

Every request carries your license key in a header.

X-License-Key: tmpt_live_xxxxxxxxxxxxxxxx

Authorization: Bearer tmpt_live_… works too. Requests without a valid key are rejected before any work is done.

Response headers

HeaderMeaning
X-License-PlanYour plan name.
X-Usage-UsedRequests consumed so far.
X-Usage-LimitYour quota, or prepaid credit balance.
X-License-ProductThe product this route billed against.

Key scopes

A key is scoped to the products it may call. Calling a route your key is not scoped to returns 403 and costs nothing — no credit, no rate budget.

ScopeUnlocks
tmptPOST /api/captcha/tmpt, POST /api/captcha/recaptcha
botdeflectPOST /api/botdeflect/pow, POST /api/botdeflect/icon

Your enabled scopes are listed on the API Key page of your dashboard. To add one, get in touch and we enable it on your existing key — no rotation needed.

{ "success": false, "error": "license not enabled for botdeflect" }

Errors

Every error is a JSON body with success: false and a human-readable error.

{ "success": false, "error": "proxy required" }
HTTPMeaning
200 Handled. Check success — a well-formed request whose challenge did not yield is a 200 with success: false.
400 Malformed request: a missing or invalid field, bad JSON, or a body over the size cap.
401License key missing or invalid.
402Quota exhausted / out of credits.
403Key revoked, expired, or not scoped to this product.
429Per-second rate limit exceeded — retry shortly.
503Solver queue saturated or licensing backend unavailable. Retry.

We drive the whole token flow behind your proxy and hand back the cookie value plus the browser identity it was minted for. Replay both together.

Mint a token

POST /api/captcha/tmpt tmpt
→ request
{
  "proxy":  "http://user:pass@host:port",
  "target": "https://www.eticketing.co.uk",
  "action": "Event"
}
← response
{
  "success": true,
  "token": "Oeqx…",
  "session_id": "a1b2c3…",
  "fingerprint": { /* see below */ }
}

Request body

FieldTypeDescription
proxystringrequired Proxy used to mint the token, as http://user:pass@host:port or http://host:port. Replay the token through this same proxy.
targetstringrequired The site the token is for, e.g. https://www.eticketing.co.uk. Origin and host are derived from it. url is accepted as an alias.
actionstringoptional Flow action. Defaults to LoginPage; Event is the other common one.
user_agentstringoptional Mint the token for this exact User-Agent. The sec_ch_ua* hints are derived from it unless you send them explicitly. Omit it and a random desktop profile is used.
sec_ch_uastringoptional Pin Sec-CH-UA instead of deriving it.
sec_ch_ua_platformstringoptional Pin Sec-CH-UA-Platform, e.g. "Windows", "macOS".
sec_ch_ua_mobilestringoptional Pin Sec-CH-UA-Mobile, e.g. ?0.
accept_languagestringoptional Pin Accept-Language, e.g. en-US,en;q=0.9.
site_keystringoptional reCAPTCHA site key. Defaults to the standard eps site key. sitekey is accepted as an alias.

Success response

{
  "success": true,
  "token": "Oeqx…the-tmpt-cookie-value…",
  "session_id": "a1b2c3…",
  "fingerprint": {
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) … Chrome/148.0.0.0 Safari/537.36",
    "accept_language": "en-US,en;q=0.9",
    "sec_ch_ua": "\"Chromium\";v=\"148\", \"Google Chrome\";v=\"148\", \"Not/A)Brand\";v=\"99\"",
    "sec_ch_ua_mobile": "?0",
    "sec_ch_ua_platform": "\"Windows\""
  }
}
FieldDescription
tokenThe value to set as the tmpt cookie on your request to the target.
session_idThe eps session id bound to the token (informational).
fingerprintThe browser identity the token was minted for. Replay these exact headers with it.

Replay the token

On your request to target, send the cookie tmpt=<token> together with the user_agent, accept_language and sec_ch_ua* values exactly as returned in fingerprint — and through the same proxy. A token replayed under a different identity is a token wasted.

# mint
curl -X POST https://api.antibot.primeseats.dev/api/captcha/tmpt \
  -H "X-License-Key: tmpt_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "proxy": "http://user:pass@host:port",
    "target": "https://www.eticketing.co.uk",
    "action": "Event"
  }'

Pin an identity

Currently not honoured. Tokens are minted by a real browser, and the token only stays valid under that browser's User-Agent and client hints: a token replayed under a different identity yields a cookie the target rejects. The identity fields below are still accepted for compatibility, but the response fingerprint is authoritative — switch your session to it after every mint (User-Agent, sec-ch-ua*, accept-language).

{
  "proxy": "http://user:pass@host:port",
  "target": "https://www.eticketing.co.uk",
  "action": "Event",
  "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
  "sec_ch_ua": "\"Chromium\";v=\"148\", \"Google Chrome\";v=\"148\", \"Not/A)Brand\";v=\"99\"",
  "sec_ch_ua_platform": "\"macOS\"",
  "sec_ch_ua_mobile": "?0",
  "accept_language": "en-US,en;q=0.9"
}

reCAPTCHA v3 token

Ask for just the reCAPTCHA v3 token and run the rest yourself. No proxy is needed; the token is valid for about two minutes and is not bound to an IP. Costs one solve, like /api/captcha/tmpt. Two kinds of site key are supported, picked with the enterprise field.

FieldTypeDescription
targetstringrequired Page URL the key is used on. The token is minted for its hostname.
actionstringoptional The action string the site passes to reCAPTCHA. Defaults to LoginPage.
enterprisebooleanoptional true (default) for reCAPTCHA Enterprise keys such as Ticketmaster's; false for a plain v3 key loaded from api.js?render=<key>.
site_keystringoptional The site key. Defaults to the eps key for Enterprise; required when enterprise is false.
POST /api/captcha/recaptcha tmpt
→ request · plain v3 key
{
  "target": "https://example.com/login",
  "site_key": "6Lc…",
  "action": "login",
  "enterprise": false
}
← response
{
  "success": true,
  "token": "0cAFcWeA…",
  "fingerprint": { /* identity the token was minted under */ }
}

Send the token to the site's own verify endpoint, with the same fingerprint if it cares about the browser identity. The same optional identity fields as above (user_agent and the client hints) are accepted; fingerprint echoes either your pinned identity or the browser identity the token was minted under. For a Ticketmaster-family site, leave enterprise out: GET /eps-mgr on the target, then POST /epsf/gec/v3/<action> with {"hostname","key","token"} on the same session, and the tmpt cookie comes back on the response.

BotDeflector is queue-it's newer challenge system, separate from the older /challengeapi/… challenges. A softblock page hands you a flow JWT pointing at dual.challenge.queue-it.net, which serves a chain of sub-challenges declared inside that token. Each solved sub-challenge returns a token; the tokens are exchanged for a ChallengeFlowResult, which the site's own verify endpoint accepts.

These endpoints are compute-only. They make no outbound requests — you fetch the challenge and post the verify yourself, so your proxy, TLS fingerprint and header order stay exactly as you built them. We only run the part that costs CPU: the cuckoo-cycle search and the icon matching.

The flow

Decode botDeflectorJwtToken from the softblock page and walk its challengeFlow array in order. Flow type inv maps to the pow endpoint, icon to icon.

  1. 1
    you
    GET dual.challenge.queue-it.net/{endpoint}/get

    Send x-tt-flow-token: <flow JWT>. Returns a challengeToken — itself a JWT carrying the challenge parameters.

  2. 2
    this api
    POST /api/botdeflect/pow  ·  POST /api/botdeflect/icon

    Hand us the challengeToken (and, for icon, the two images). You get the solution back in milliseconds.

  3. 3
    you
    POST dual.challenge.queue-it.net/{endpoint}/verify

    Post the solution together with the challengeToken and the flow token. Returns a solution token. Repeat 1–3 for every step in the flow.

  4. 4
    you
    GET dual.challenge.queue-it.net/verify-flow

    Send every solution token in x-tt-solution-tokens, comma-joined, in the order the flow declared them. Returns the ChallengeFlowResult.

  5. 5
    you
    POST {queue-domain}/…/challenge/botdeflector/verify

    Hand the flow result to the site's own verify endpoint. Returns the sessionInfo you carry into the queue re-entry as scv.

Proof of work

Flow type inv. A cuckoo-cycle search over a graph seeded by the challenge session.

POST /api/botdeflect/pow botdeflect
→ request
{
  "challenge_token": "eyJhbGciOi…"
}
← response
{
  "success": true,
  "solution": [
    { "challenge": "9f3c…",
      "edgeIndices": [812,1907,44,3311,77,2560] }
  ],
  "params": {
    "NODES_BITS": 12, "EDGES_BITS": 13,
    "CYCLE_LENGTH": 6, "GRAPH_SEED": "c8f4…"
  },
  "time": "0.002",
  "solve_ms": 2
}

Request body

FieldTypeDescription
challenge_tokenstringrequired* The challengeToken from /pow/get. We read the challenge hashes, the params and the session id (which seeds the graph) straight off it. Only the payload is decoded — the signature is never touched.
challengesstring[]optional Solve these challenge hashes instead of the token's. Hex.
paramsobjectoptional Override individual graph parameters: NODES_BITS, EDGES_BITS, CYCLE_LENGTH, GRAPH_SEED. Anything you omit falls back to the token, then to the defaults 12 / 13 / 6.

* challenge_token or challenges — one of the two. Explicit fields always win over the token.

Response

FieldDescription
solutionThe solution array /pow/verify expects, verbatim — one closed cycle per challenge hash, in the order given.
paramsThe merged parameters the solution was computed against, GRAPH_SEED included. Send this back in the verify body — the verifier rebuilds the graph from it, and a mismatch fails the check.
timeSolve duration in seconds, pre-formatted for the verify body's time field.
solve_msThe same duration in milliseconds (informational).

Building the verify body

{
  "solution":             // from our response
  "params":               // from our response, verbatim
  "time":                 // from our response
  "solution_fingerprint": "<your FingerprintJS visitorId>",
  "challengeToken":       "<from /pow/get>",
  "challengeFlowToken":   "<the flow JWT>"
}

Icon challenge

Flow type icon. Find each of the icons from the strip inside the background image, in strip order.

Fetch both images yourself using lib, img and ico from the icon challengeToken payload: /icons/{lib}/bgnd/{img} and /icons/{lib}/ico/{ico}. Send us the bytes, base64-encoded.

POST /api/botdeflect/icon botdeflect
→ request
{
  "background": "iVBORw0KGgoAAAANS…",
  "icons":      "iVBORw0KGgoAAAANS…"
}
← response
{
  "success": true,
  "solution": [
    { "x": 66,  "y": 60 },
    { "x": 167, "y": 127 },
    { "x": 245, "y": 53 }
  ],
  "solve_ms": 1
}

Request body

FieldTypeDescription
backgroundstringrequired The background image bytes, base64. A data:image/png;base64,… prefix is accepted.
iconsstringrequired The icon strip bytes, base64. Its transparency defines the icons and their order.

The body cap on this route is 4 MiB, which fits both images comfortably.

Response

solution holds one point per strip icon, in strip order — that is the order the answer must be given in. Coordinates are pixels in the background image.

When the background cannot be segmented into one region per strip icon you get success: false with a 200. Re-request the challenge rather than guessing: a wrong click is a stronger bot signal than a slow one.

Do not send solve_ms as the verify body's time. We solve an icon challenge in about a millisecond; a human needs seconds to find and click three icons. Report a plausible duration instead — the reference implementation uses a random 4500–9500 ms — or it becomes the most obvious tell in the payload.

Building the verify body

{
  "solution":             // from our response
  "time": 6420,           // milliseconds, human-plausible — NOT solve_ms
  "solution_fingerprint": "<your FingerprintJS visitorId>",
  "challengeToken":       "<from /icon/get>",
  "challengeFlowToken":   "<the flow JWT>"
}

Worked example

One proof-of-work step, end to end.

# 1. ask the challenge host for a challenge
curl 'https://dual.challenge.queue-it.net/pow/get' \
  -H "x-tt-flow-token: $FLOW_TOKEN"
# -> {"challengeToken":"eyJhbGciOi…"}

# 2. solve it here
curl -X POST https://api.antibot.primeseats.dev/api/botdeflect/pow \
  -H "X-License-Key: tmpt_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"challenge_token":"eyJhbGciOi…"}'
# -> {"success":true,"solution":[…],"params":{…},"time":"0.002"}

# 3. verify it, from your own session
curl -X POST 'https://dual.challenge.queue-it.net/pow/verify' \
  -H "Content-Type: application/json" \
  -d '{
    "solution": […],
    "params": {…},
    "time": "0.002",
    "solution_fingerprint": "'"$FINGERPRINT"'",
    "challengeToken": "eyJhbGciOi…",
    "challengeFlowToken": "'"$FLOW_TOKEN"'"
  }'
# -> {"token":"…"}  — collect it, then move to the next flow step

Health

GET /health — no key required. Reports service status, the live solver queue and the routes this deployment serves.