PopularSessionAction
Create an Epic-compatible PopularCaptcha session and get sessionId, egToken, and userAgent.
PopularSessionAction is the first step for Epic Games PopularCaptcha Enterprise flows (login, password reset). It creates a session-bound device fingerprint and returns:
solution.sessionId— pass into later PopularCaptcha tasks astask.sessionIdsolution.egToken— use as the Talonxalvaluesolution.userAgent— must be used exactly for all Talon + Epic requests in the same flow
This is a multi-step integration
Epic validates TLS fingerprint + headers + request formatting in addition to captcha correctness. If your Talon flow is incorrect, the captcha solve can still be rejected.
How it works
PopularSessionAction — create a session and obtain sessionId, egToken, and userAgent.
Talon flow (client-side) — use egToken + userAgent with a mobile-grade TLS fingerprint to produce
rqdata.
PopularCaptchaEnterpriseToken — solve the captcha by sending rqdata + sessionId (and your proxy).
Submit immediately — poll getTaskResult, then submit the token to Epic with the correct payload.
Step 1 — Create the session (AnySolver)
Create a task of type PopularSessionAction.
{
"clientKey": "YOUR_ANYSOLVER_KEY",
"provider": "OnyxSolver",
"task": {
"type": "PopularSessionAction",
"proxyRegion": "gb"
}
}Then fetch the result (usually ready quickly):
{
"clientKey": "YOUR_ANYSOLVER_KEY",
"taskId": "TASK_ID_FROM_CREATE"
}You will receive:
solution.sessionIdsolution.egTokensolution.userAgent
Keep values consistent
Use the returned userAgent value everywhere in the Talon flow and all Epic requests for that session. Do not reuse
egToken across sessions.
Step 2 — Talon flow (client-side)
The Talon flow is a 7-step sequence that produces rqdata. You run it on your side using the egToken + userAgent from Step 1.
Talon steps (high level)
GET /id/api/i18n— obtain theXSRF-TOKENcookieGETtracking pixel — analytics (non-critical, but recommended)POST /id/api/analytics—APP_INITPOST /id/api/analytics—PAGE_OPENPOST talon-service /v1/init— initialize session- wait ~2 seconds
POST talon-service /v1/init/execute— sendegTokenasxal, receiverqdata
HTTP client requirements
Epic validates TLS and headers. Use a client that can impersonate a real mobile TLS handshake (for example curl_cffi).
Compact JSON bodies
Some Epic endpoints validate exact JSON formatting. Use compact serialization (no spaces): json.dumps(body, separators=(',', ':')), and send with data= (bytes) rather than json=.
# pip install curl_cffi
def get_tls_impersonate(ua: str) -> str:
# Android Chrome UA
if "Chrome" in ua and "Firefox" not in ua:
return "chrome131"
# Firefox UA
return "firefox133"import json
from curl_cffi.requests import AsyncSession
ua = session_data["userAgent"]
eg_token = session_data["egToken"]
impersonate = get_tls_impersonate(ua)
headers = {
"User-Agent": ua,
"Accept": "application/json, text/plain, */*",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"Origin": "https://www.example.com"
}
body = {"eventType": "APP_INIT"}
async with AsyncSession(impersonate=impersonate, proxy=proxy, timeout=30) as s:
resp = await s.post(url, headers=headers, data=json.dumps(body, separators=(",", ":")).encode())Required headers (Epic API)
You typically need these headers on analytics + login/reset requests (the X-XSRF-TOKEN comes from the cookie in step 2.1):
headers = {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json",
"User-Agent": user_agent, # from PopularSessionAction
"X-Epic-Access-Key": "undefined",
"X-Epic-Client-ID": "undefined",
"X-Epic-Display-Mode": "web",
"X-Epic-Duration": str(duration_ms),
"X-Epic-Event-Action": "login", # or "passwordReset"
"X-Epic-Event-Category": "login", # or "recovery"
"X-Epic-Flow": "login", # or "password_reset"
"X-Epic-Platform": "WEB",
"X-Epic-Strategy-Flags": "isolatedTestFlagEnabled=false",
"X-Requested-With": "XMLHttpRequest",
"X-XSRF-TOKEN": xsrf_token,
"Origin": "https://www.example.com",
"Referer": "https://www.example.com/id/login"
}Step 3 — Solve the captcha (AnySolver)
After you obtain rqdata, create an enterprise PopularCaptcha task and pass both rqdata and the sessionId from Step 1.
This is the most common variant for Epic flows because IP/session matching is enforced:
{
"clientKey": "YOUR_ANYSOLVER_KEY",
"provider": "OnyxSolver",
"task": {
"type": "PopularCaptchaEnterpriseToken",
"websiteURL": "https://www.example.com/login",
"websiteKey": "YOUR_SITEKEY",
"rqdata": "RADATA_FROM_TALON_EXECUTE",
"sessionId": "SESSION_ID_FROM_POPULAR_SESSION_ACTION",
"proxy": {
"type": "http",
"host": "1.2.3.4",
"port": 8080,
"username": "user",
"password": "pass"
}
}
}Sitekey note
Epic can use different sitekeys per page (login vs password reset). Always extract the sitekey from the exact page you are solving.
Step 4 — Get result and submit
Poll getTaskResult until status is ready. Then submit immediately.
Epic typically expects a base64-encoded captcha payload that includes the Talon session wrapper plus the captcha token and your egToken.
import json, base64
captcha_payload = {
"session_wrapper": talon_session, # from Talon /v1/init (updated if execute returns a new session)
"plan_results": {
"h_captcha": {
"value": captcha_token, # from AnySolver getTaskResult
"resp_key": ""
}
},
"xal": eg_token, # from PopularSessionAction
"ewa": "b",
"kid": "Yjqmlr"
}
captcha_b64 = base64.b64encode(json.dumps(captcha_payload, separators=(",", ":")).encode()).decode()Proxy/IP consistency
Use a sticky proxy and keep the same IP across: Talon flow → captcha solve → Epic submission.
Related tasks
Using sessionId in PopularCaptcha
The task.sessionId field on PopularCaptcha task types is intended to be populated from the PopularSessionAction
response.
Supported Providers
| Provider | Price per 1,000 |
|---|---|
| $0.10 |
Request Schema
| Field | Type | Description |
|---|---|---|
clientKey* | string | Your API key from the AnySolver dashboard. Example: |
task* | object | |
provider | Specific provider to use. If omitted, automatic routing selects the best provider. Example: | |
selectionMode | Read more at the routing strategies docs. Example: | |
keyPoolMode | Example: |
Task Object Properties
The task field accepts an object with the following properties:
| Field | Type | Description |
|---|---|---|
proxyRegion* | string | Proxy region code for session creation (e.g. "gb", "us"). Example: |
Example Request
{ "clientKey": "your-api-key-from-dashboard", "task": { "proxyRegion": "gb", "type": "PopularSessionAction" }}Response Schema
| Field | Type | Description |
|---|---|---|
status* | Task status: "processing", "ready", or "failed". Example: | |
taskId | string | Unique identifier returned when the task was created. Example: |
errorId* | 0 = success, 1 = external error, 2 = internal error. Example: | |
errorCode | Machine-readable error code (e.g., "CAPTCHA_UNSOLVABLE"). Example: | |
errorDescription | string | Human-readable error message with resolution hints. Example: |
cost | number | Actual cost charged for this task in USD. Example: |
taskType | The type of CAPTCHA task to solve. Example: | |
provider | Specific provider to use. If omitted, automatic routing selects the best provider. Example: | |
solution | object |
Solution Object Properties
The solution field contains an object with the following properties:
| Field | Type | Description |
|---|---|---|
sessionId* | string | The session ID to use for subsequent PopularCaptcha tasks. Example: |
egToken* | string | Encrypted token returned by the session creation endpoint. Example: |
userAgent* | string | User-Agent string associated with this session. Example: |
raw* | Record<string, unknown> | Raw provider response data for advanced use cases. |